【发布时间】:2022-01-08 18:06:25
【问题描述】:
假设我们有一个双精度数组x 和一个索引数组y,并且我们希望通过x 中的相应值对这些索引进行排序(因此,将[i in y] 排序为x[i] 作为键)。
然后我们可以创建一个对数组,其中一个组件是键值,一个是索引,例如,执行以下操作:
boost::sort::spreadsort::float_sort(sortdata, sortdata + n,
[](const std::pair<double, int> &a, const unsigned offset) -> boost::int64_t {
return boost::sort::spreadsort::float_mem_cast<double, boost::int64_t>(a.first) >> offset;
},
[](const std::pair<double, int> &a, const std::pair<double, int> &b) -> bool {
return a.first < b.first;
});
这需要相当多的内存,所以我们可以省略创建这个对数组,直接使用这样的数据:
boost::sort::spreadsort::float_sort(y, y + n,
[x](const int a, const unsigned offset) -> boost::int64_t {
return boost::sort::spreadsort::float_mem_cast<double, boost::int64_t>(x[a]) >> offset;
},
[x](const int a, const int b) -> bool {
return x[a] < x[b];
});
现在,当在非常大的数据(比如 50000000 个条目)上使用它时,第二种方法所需的时间是第一种方法的两倍多。据我所知,std::pair 只是struct。那么,数组访问是否真的比结构访问效率低得多?或者,我在这里做错了什么?
这是一个完整的比较示例:
#include <cstdlib>
#include <ctime>
#include <boost/sort/spreadsort/spreadsort.hpp>
#include <iostream>
int main() {
int n = 50000000;
double *x = new double[n];
int *y = new int[n];
std::pair<double, int> *sortdata = new std::pair<double, int> [n];
for (int i=0; i < n; i++) {
x[i] = ((double) std::rand()) / ((double) std::rand());
sortdata[i].first = x[i];
y[i] = i;
sortdata[i].second = i;
}
std::time_t t = std::time(0);
boost::sort::spreadsort::float_sort(sortdata, sortdata + n,
[](const std::pair<double, int> &a, const unsigned offset) -> boost::int64_t {
return boost::sort::spreadsort::float_mem_cast<double, boost::int64_t>(a.first) >> offset;
},
[](const std::pair<double, int> &a, const std::pair<double, int> &b) -> bool {
return a.first < b.first;
});
std::cout << std::time(0)-t << "\n";
t = std::time(0);
boost::sort::spreadsort::float_sort(y, y + n,
[x](const int a, const unsigned offset) -> boost::int64_t {
return boost::sort::spreadsort::float_mem_cast<double, boost::int64_t>(x[a]) >> offset;
},
[x](const int a, const int b) -> bool {
return x[a] < x[b];
});
std::cout << std::time(0)-t << "\n";
}
在我的系统上使用g++ -O2 运行此程序会在第一时间为 3 秒,为最后一个时间为 9 秒。
【问题讨论】:
-
如果没有完整的minimal reproducible example,不可能有权威的答案,但在我看来,第一种情况直接比较值,而第二种情况有间接比较。这实际上会产生相当多的开销。
-
您记得启用优化吗?
-
@OP 任何有关 C++ 代码性能的问题都必须附带您用于构建程序的编译器选项。如果您正在运行“调试”或未优化的构建,那么您向我们展示的时间信息是没有意义的。
-
显然
a.first < b.first看起来比x[a] < x[b]快,无论x是什么类型——vector/set/map/etc 取消引用都会比访问已经可用的值慢,而不是提到x被按值捕获(副本)。 -
问题不在于取消引用值的成本,也不是结构与数组的成本,而是内存访问模式。并且对于输入数组,第二种方法可以比前者更快或更慢。如果数组包含随机值,前者将比第二个(执行不可预测的内存随机访问)快得多。对于像您的示例中那样不适合缓存的大数组尤其如此。请注意,排序算法也很重要(因为比较与副本的数量会发生变化)。
标签: c++ arrays performance struct std-pair