【问题标题】:Is "==" in sorted array not faster than unsorted array? [duplicate]排序数组中的“==”不比未排序数组快吗? [复制]
【发布时间】:2015-11-10 20:21:46
【问题描述】:

注意:我认为,所谓的重复问题主要与“”比较有关,但与“==”比较无关,因此没有回答我关于“==”性能的问题" 运算符。

长期以来,我一直认为“处理”已排序的数组应该比未排序的数组更快。起初,我认为在排序数组中使用“==”应该比在未排序数组中更快,因为 - 我猜 - 分支预测的工作原理:

未排序数组:

5 == 100 F
43 == 100 F
100 == 100 T
250 == 100 F
6 == 100 F
(other elements to check)

排序数组:

5 == 100 F
6 == 100 F
43 == 100 F
100 == 100 T
(no need to check other elements, so all are F)

所以我猜SORTEDARRAY应该比UNSORTEDARRAY快,但是今天我用代码在一个header中生成了2个数组来测试,分支预测似乎并没有像我想象的那样工作。

我生成了一个未排序的数组和一个排序的数组来测试:

srand(time(NULL));
int UNSORTEDARRAY[524288];
int SORTEDARRAY[sizeof(UNSORTEDARRAY)/sizeof(int)];
for(int i=0;i<sizeof(SORTEDARRAY)/sizeof(int);i++){
    SORTEDARRAY[i]=UNSORTEDARRAY[i]=rand();
}
sort(SORTEDARRAY,SORTEDARRAY+sizeof(SORTEDARRAY)/sizeof(int));
string u="const int UNSORTEDARRAY[]={";
string s="const int SORTEDARRAY[]={";
for(int i=0;i<sizeof(UNSORTEDARRAY)/sizeof(int);i++){
    u+=to_string(UNSORTEDARRAY[i])+",";
    s+=to_string(SORTEDARRAY[i])+",";
}
u.erase(u.end()-1);
s.erase(s.end()-1);
u+="};\n";
s+="};\n";
ofstream out("number.h");
string code=u+s;
out << code;
out.close();

所以要测试,只要像这样计算值是否 == RAND_MAX/2:

#include "number.h"
int main(){
int count;
    clock_t start = clock();
    for(int i=0;i<sizeof(SORTEDARRAY)/sizeof(int);i++){
        if(SORTEDARRAY[i]==RAND_MAX/2){
            count++;
        }
    }
    printf("%f\n",(float)(clock()-start)/CLOCKS_PER_SEC);
}

运行 3 次:

未排序数组

0.005376
0.005239
0.005220

排序数组

0.005334
0.005120
0.005223

这似乎是一个小的性能差异,所以我不相信它然后尝试将“SORTEDARRAY[i]==RAND_MAX/2”更改为“SORTEDARRAY[i]>RAND_MAX/2”,看看它是否做出了区别:

未排序数组

0.008407
0.008363
0.008606

排序数组

0.005306
0.005227
0.005146

这次有很大的不同。

排序数组中的“==”不比未排序数组快吗?如果是,为什么排序数组中的“>”比未排序数组快,而“==”不是?

【问题讨论】:

  • 与有史以来最受欢迎的问题之一有关:stackoverflow.com/questions/11227809/…
  • “我相信“处理”一个排序数组应该比未排序数组更快”:试着回答自己为什么你认为这个算法是正确的。那就是-您为每种情况做什么样的工作以及做多少工作。您可能会意识到答案是什么。
  • string 不是 C 中的标准类型,并且将 += 运算符与一个 string 类型的操作数和另一个 char * 类型一起使用是没有意义的。你确定这不是 C++ 代码吗?
  • 另外,你用什么来计时这段代码?一些非常不准确的东西,并且可能有偏见。这种问题通常是被误导的人写的你甚至启用了完全优化吗?你有一个实际的问题要解决,以及解决这个问题的程序吗?您是否在该程序上使用分析器来确定重大瓶颈是什么?我问的原因是,在任何实际情况下,瓶颈都会与您所描述的有很大不同。这个问题没有实际用处。
  • 你为什么假设“(不需要检查其他元素,所以都是F)”?编译器无法知道这一点,它只会盲目地检查每个内存位置。事实上,使用随机数据,它很少会等于一个固定值,因此很容易被 CPU 预测。

标签: c arrays performance optimization sortedlist


【解决方案1】:

立即想到的一件事是 CPU 的分支预测算法。

&gt; 比较的情况下,排序数组中的分支行为非常一致:首先,if 条件始终为假,然后始终为真。即使是最简单的分支预测,这也非常吻合。

在未排序的数组中,&gt; 条件的结果本质上是随机的,因此阻碍了任何分支预测。

这就是使排序版本更快的原因。

== 比较的情况下,大多数情况下条件为假,只有极少数情况下为真。无论数组是否已排序,这都适用于分支预测。时间基本相同。

【讨论】:

    【解决方案2】:

    注意我正在回答这个问题,因为评论太长了。

    这里的效果正是this question的大量答案中已经详细解释的。由于分支预测,在这种情况下处理排序数组会更快。

    这里,罪魁祸首又是分支预测。 == 测试很少为真,因此分支预测对两者的工作方式大致相同。当您将其更改为 &gt; 时,您会得到该问题中解释的行为,并且出于相同的原因。


    道德:

    我相信“处理”一个排序数组应该比 [an ]unsorted 数组更快。

    你需要知道为什么。这不是什么神奇的规则,也不一定总是正确的。

    【讨论】:

      【解决方案3】:

      比较== 与排序的关系不如&gt;。正确或错误地预测 == 仅取决于重复值的数量及其分布。

      您可以使用perf stat 查看性能计数器...

      jason@io /tmp $ lz4 -d ints | perf stat ./proc-eq >/dev/null
      Successfully decoded 104824717 bytes
      
       Performance counter stats for './proc-eq':
      
             5226.932577      task-clock (msec)         #    0.953 CPUs utilized
                      31      context-switches          #    0.006 K/sec
                      24      cpu-migrations            #    0.005 K/sec
                   3,479      page-faults               #    0.666 K/sec
          15,763,486,767      cycles                    #    3.016 GHz
           4,238,973,549      stalled-cycles-frontend   #   26.89% frontend cycles idle
         <not supported>      stalled-cycles-backend
          31,522,072,416      instructions              #    2.00  insns per cycle
                                                        #    0.13  stalled cycles per insn
           8,515,545,178      branches                  # 1629.167 M/sec
              10,261,743      branch-misses             #    0.12% of all branches
      
             5.483071045 seconds time elapsed
      
      jason@io /tmp $ lz4 -d ints | sort -n | perf stat ./proc-eq >/dev/null
      Successfully decoded 104824717 bytes
      
       Performance counter stats for './proc-eq':
      
             5536.031410      task-clock (msec)         #    0.348 CPUs utilized
                     198      context-switches          #    0.036 K/sec
                      21      cpu-migrations            #    0.004 K/sec
                   3,604      page-faults               #    0.651 K/sec
          16,870,541,124      cycles                    #    3.047 GHz
           5,300,218,855      stalled-cycles-frontend   #   31.42% frontend cycles idle
         <not supported>      stalled-cycles-backend
          31,526,006,118      instructions              #    1.87  insns per cycle
                                                        #    0.17  stalled cycles per insn
           8,516,336,829      branches                  # 1538.347 M/sec
              10,980,571      branch-misses             #    0.13% of all branches
      
      jason@io /tmp $ lz4 -d ints | perf stat ./proc-gt >/dev/null
      Successfully decoded 104824717 bytes
      
       Performance counter stats for './proc-gt':
      
             5293.065703      task-clock (msec)         #    0.957 CPUs utilized
                      38      context-switches          #    0.007 K/sec
                      50      cpu-migrations            #    0.009 K/sec
                   3,466      page-faults               #    0.655 K/sec
          15,972,451,322      cycles                    #    3.018 GHz
           4,350,726,606      stalled-cycles-frontend   #   27.24% frontend cycles idle
         <not supported>      stalled-cycles-backend
          31,537,365,299      instructions              #    1.97  insns per cycle
                                                        #    0.14  stalled cycles per insn
           8,515,606,640      branches                  # 1608.823 M/sec
              15,241,198      branch-misses             #    0.18% of all branches
      
             5.532285374 seconds time elapsed
      
      jason@io /tmp $ lz4 -d ints | sort -n | perf stat ./proc-gt >/dev/null
      
            15.930144154 seconds time elapsed
      
       Performance counter stats for './proc-gt':
      
             5203.873321      task-clock (msec)         #    0.339 CPUs utilized
                       7      context-switches          #    0.001 K/sec
                      22      cpu-migrations            #    0.004 K/sec
                   3,459      page-faults               #    0.665 K/sec
          15,830,273,846      cycles                    #    3.042 GHz
           4,456,369,958      stalled-cycles-frontend   #   28.15% frontend cycles idle
         <not supported>      stalled-cycles-backend
          31,540,409,224      instructions              #    1.99  insns per cycle
                                                        #    0.14  stalled cycles per insn
           8,516,186,042      branches                  # 1636.509 M/sec
              10,205,058      branch-misses             #    0.12% of all branches
      
            15.365528326 seconds time elapsed
      

      【讨论】:

        猜你喜欢
        • 2012-10-02
        • 1970-01-01
        • 1970-01-01
        • 2012-06-28
        相关资源
        最近更新 更多