C++ 在数组中查找两个最小int64元素的最快方法
我有大小从1000到10000(1k..10k)的阵列。每个元素都是int64。我的任务是找到数组中两个最小的元素,最小元素和剩余元素中的最小元素 <>我想在C++中获得最快的单线程代码,英特尔CORE2或CORIE7(CPU模式为64位)。p> 这个函数(从数组中获取最小的2个)是热点,它嵌套在两个或三个循环中,用于迭代次数巨大的循环 当前代码如下所示:C++ 在数组中查找两个最小int64元素的最快方法,c++,optimization,minimum,C++,Optimization,Minimum,我有大小从1000到10000(1k..10k)的阵列。每个元素都是int64。我的任务是找到数组中两个最小的元素,最小元素和剩余元素中的最小元素 我想在C++中获得最快的单线程代码,英特尔CORE2或CORIE7(CPU模式为64位)。p> 这个函数(从数组中获取最小的2个)是热点,它嵌套在两个或三个循环中,用于迭代次数巨大的循环 当前代码如下所示: int f() { int best; // index of the minimum element int64 min_co
int f()
{
int best; // index of the minimum element
int64 min_cost = 1LL << 61;
int64 second_min_cost = 1LL << 62;
for (int i = 1; i < width; i++) {
int64 cost = get_ith_element_from_array(i); // it is inlined
if (cost < min_cost) {
best = i;
second_min_cost = min_cost;
min_cost = cost;
} else if (cost < second_min_cost) {
second_min_cost = cost;
}
}
save_min_and_next(min_cost, best, second_min_cost);
}
intf()
{
int best;//最小元素的索引
int64 min_cost=1LL您拥有的是O(n)
,对于随机数据来说是最佳的。这意味着,您已经拥有了最快的
改善这一点的唯一方法是为数组指定某些属性,例如,始终保持数组的排序或使其成为一个堆。好的一点是,您的算法只扫描一次数字。您是最佳的
<> P>一个重要的慢度来源可能来自元素排列方式,如果它们是数组,我是指C数组(或者C++向量)如果所有元素都是连续的,并且你向前扫描它们,那么内存方面你也是最优的。否则,你可能会有一些惊喜。例如,如果你的元素在一个链表中,或者分散在一起,那么你可能会受到内存访问的惩罚。查看并
std::vector arr(10000);//大
部分排序(arr.begin(),arr.begin()+2,arr.end());
//arr[0]和arr[1]是最小的两个值
如果您只需要第二个最低值,那么第n个元素就是您的对象请确保您的数组读取行为正常,这样就不会引入不必要的缓存未命中
假设阵列读取很简单,则此代码可能非常接近现代CPU:s上的带宽限制。您需要分析和/或计算它是否仍有CPU优化空间。尝试反转if:
if (cost < second_min_cost)
{
if (cost < min_cost)
{
}
else
{
}
}
if(成本
您可能应该使用相同的值初始化min_cost和second_min_cost,使用int64的最大值(或者更好地使用qbert220的建议)一些小事情(可能已经发生了,但我想可能值得一试)
稍微展开循环-例如,以8的步幅迭代(即一次迭代缓存线),预取正文中的下一个缓存线,然后处理8项。为避免大量检查,请确保结束条件是8的倍数,剩余项(小于8)应在循环外处理-展开
对于不感兴趣的项目,您在正文中进行了两次检查,可能是您可以将成本调整为1?即,如果成本小于秒分钟
,则也检查分钟
,否则无需麻烦
您最好先检查second_min_cost,因为这是修改结果所需的唯一条件。这样,您将在主循环中得到一个分支,而不是两个分支。这应该会有很大帮助
除此之外,没有什么可以优化的,您的已接近最优。展开可能会有所帮助,但我怀疑它在这种情况下是否会带来任何显著优势
因此,它变成:
int f()
{
int best; // index of the minimum element
int64 min_cost = 1LL << 61;
int64 second_min_cost = 1LL << 62;
for (int i = 1; i < width; i++) {
int64 cost = get_ith_element_from_array(i); // it is inlined
if (cost < second_min_cost)
{
if (cost < min_cost)
{
best = i;
second_min_cost = min_cost;
min_cost = cost;
}
else second_min_cost = cost;
}
save_min_and_next(min_cost, best, second_min_cost);
}
intf()
{
int best;//最小元素的索引
int64 min_cost=1LL OP显然对O(n)
范围内的优化感兴趣,5*n操作和10*n操作都是O(n)
,但其中一个显然比另一个快。简单的大O符号分析在这里似乎不够。你可以用数组中的第一个条目初始化最小成本。我还注意到你目前只循环一次(宽度-1)时间,这可能不是预期的行为。最好使用数组的前两个元素初始化min_cost和second_min_cost,从i=2开始循环(当然,这是假设数组至少有两个元素)我认为这在很大程度上取决于从数组中获取元素的功能。如果它实际访问的数组大小宽度,那么应该考虑缓存行为(特别是,如果要循环超过10k的内存数百万次,那么可能会有一些重叠,因此最重要的优化可能是为这个循环之外的2或3个循环选择最佳顺序)。如果它是从i
计算值,那么内存性能可能完全无关。Steve的“从数组中获取元素”如下所示:“返回m[global\u j][i]-n[i]
”@osgx:因此,如果global_j
在这个内部循环的不同运行之间有所不同,那么通过确保以global_j
的相等值连续运行,您可能会得到一个很好的优化。这样,当您再次使用它时,m[global_j]
仍将被缓存应该在循环之外处理-展开…-以及开始时达夫的设备!@Steve:我认为达夫的设备(以及手动展开)已被现代编译器淘汰:)?@Matthieu:有时手动展开(带或不带达夫)对于给定的基准测试或给定的实际用途,提供比优化器更快的代码。现代优化技术所取得的成就是使您无法自信地预测它是否有帮助,考虑到总是会有病态的用例来击败特定的优化,这一点与它所得到的一样好Steve Jessop,作为一名对编译器内部有点了解的程序员,我可以说Duff设备对编译器来说是一场噩梦,因为它是非常非线性的(在控制流图中)。大多数编译器都会尝试检测达夫并将其回滚到正常循环。甚至有时Xfree也会用简单的循环替换所有达夫。+1,你说得对,我误读了文档
int f()
{
int best; // index of the minimum element
int64 min_cost = 1LL << 61;
int64 second_min_cost = 1LL << 62;
for (int i = 1; i < width; i++) {
int64 cost = get_ith_element_from_array(i); // it is inlined
if (cost < second_min_cost)
{
if (cost < min_cost)
{
best = i;
second_min_cost = min_cost;
min_cost = cost;
}
else second_min_cost = cost;
}
save_min_and_next(min_cost, best, second_min_cost);
}