在数组中找到两个最小int64元素的最快方法

时间:2011-10-17 11:59:30

标签: c++ optimization minimum

我有大小从1000到10000(1k ... 10k)的数组。每个元素都是int64。我的任务是找到数组的两个最小元素,最小元素和剩余的最小元素。

我希望在C ++中为Intel Core2或Corei7获得最快的单线程代码(cpu模式为64位)。

这个函数(从数组中得到2个最小的)是热点,它嵌套在两个或三个for循环中,迭代次数很多。

目前的代码如下:

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);
}

7 个答案:

答案 0 :(得分:8)

查看partial_sortnth_element

std::vector<int64_t> arr(10000); // large

std::partial_sort(arr.begin(), arr.begin()+2, arr.end());
// arr[0] and arr[1] are minimum two values

如果你只想要第二个最低值,那么nth_element就是你的家伙

答案 1 :(得分:5)

尝试反转if:

if (cost < second_min_cost) 
{ 
    if (cost < min_cost) 
    { 
    } 
    else
    {
    }
} 

你应该使用相同的值初始化min_cost和second_min_cost,使用int64的最大值(或者更好地使用qbert220的建议)

答案 2 :(得分:3)

一些小事(可能已经发生,但我猜可能值得尝试)。

  1. 稍微展开循环 - 例如,以8的步幅迭代(即一次缓存行),预取主体中的下一个缓存行,然后处理8个项目。为避免大量检查,请确保结束条件是8的倍数,并且应在循环外处理剩余项目(小于8) - 展开...

  2. 对于没有兴趣的物品,你在身体上做两次检查,可能你可以修剪到1?即如果cost小于second_min,那么也请检查min - 否则无需费心......

答案 3 :(得分:2)

您最好首先检查second_min_cost,因为它是唯一需要修改结果的条件。这样,您将在主循环中获得一个分支,而不是2分支。这应该会有所帮助。

除此之外,几乎没有优化,你已经接近最优。展开可能有所帮助,但我怀疑它会在这种情况下带来任何显着的优势。

所以,它变成了:

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);
}

答案 4 :(得分:1)

你所拥有的是O(n)并且是随机数据的最佳选择。这意味着,你已经拥有最快的。

唯一可以改进的方法是为数组提供某些属性,例如,始终对其进行排序或将其作为堆。

答案 5 :(得分:1)

好处是你的算法会扫描一次数字。你是最优的。

缓慢的一个重要原因可能来自你的元素排列方式。如果它们在一个数组中,我的意思是一个C数组(或C ++向量),其中所有元素都是连续的,你向前扫描它们,然后在内存方面你也是最优的。否则,你可能会有一些惊喜。例如,如果您的元素在链接列表中,或者收集了分散,那么您可能会因内存访问而受到惩罚。

答案 6 :(得分:1)

确保您的数组读取符合行为,因此不会引入不必要的缓存未命中。

这段代码应该非常接近现代CPU上的带宽限制:假设数组读取很简单。您需要分析和/或计算它是否仍有任何可用于CPU优化的余量。