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

Fastest way of finding two minimum int64 elements in array

本文关键字:int64 元素 方法 两个 数组      更新时间:2023-10-16

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

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

这个函数(从数组中获取最小的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);
}

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就是你要的

尝试反转if:

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

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

一些小事情(可能已经发生了,但我想可能值得一试)

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

  2. 对于不感兴趣的项目,你在身上做了两道检查,也许你可以修剪成1道?即,如果cost小于second_min,则检查min -否则无需麻烦…

您最好先检查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);
}

确保您的数组读取是正常的,这样就不会引入不必要的缓存丢失。

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

你有什么,是O(n)和最佳的随机数据。也就是说,你已经有最快的了。

唯一可以改善这一点的方法是给你的数组赋予某些属性,例如,保持它一直排序或使它成为一个堆。

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

速度慢的一个重要原因可能来自元素的排列方式。如果它们在一个数组中,我指的是一个C数组(或c++向量),其中所有元素都是连续的,你向前扫描它们,那么内存方面你也是最优的。否则,你可能会有一些惊喜。例如,如果您的元素在链表中,或者分散聚集,那么您可能会对内存访问进行惩罚。