C 是"==&引用;在排序数组中是否比未排序数组快?

C 是"==&引用;在排序数组中是否比未排序数组快?,c,arrays,performance,optimization,sortedlist,C,Arrays,Performance,Optimization,Sortedlist,注意:我认为,所谓的重复问题主要与“”比较有关,而不是“==”比较,因此没有回答我关于“==”运算符性能的问题 长期以来,我一直认为“处理”已排序数组应该比未排序数组更快。起初,我认为在排序数组中使用“==”应该比在未排序数组中使用“==”更快,因为——我猜——分支预测是如何工作的: 未排序的Darray: 5 == 100 F 43 == 100 F 100 == 100 T 250 == 100 F 6 == 100 F (other elements to check) 5 == 100

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

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

未排序的Darray:

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)
Darray:

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快,但是今天我用代码在一个报头中生成了两个数组来测试,分支预测似乎不像我想的那样有效

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

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();
分拣机

0.005376
0.005239
0.005220
0.005334
0.005120
0.005223
0.008407
0.008363
0.008606
0.005306
0.005227
0.005146
这似乎是一个很小的性能差异,所以我不相信这一点,然后尝试将“SORTEDARRAY[I]==RAND_MAX/2”更改为“SORTEDARRAY[I]>RAND_MAX/2”,看看它是否有区别:

未分类的

0.005376
0.005239
0.005220
0.005334
0.005120
0.005223
0.008407
0.008363
0.008606
0.005306
0.005227
0.005146
分拣机

0.005376
0.005239
0.005220
0.005334
0.005120
0.005223
0.008407
0.008363
0.008606
0.005306
0.005227
0.005146
这次有很大的不同


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

一件立即浮现在脑海中的事情是CPU的分支预测算法

在比较
的情况下,在排序数组中,分支行为是非常一致的:首先,
如果
条件始终为false,则它始终为true。这与即使是最简单的分支预测也非常吻合

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

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

在比较
==
的情况下,大多数情况下,条件为假,很少为真。无论数组是否排序,这都适用于分支预测。时间安排基本上是一样的。

N.B.我的回答是因为评论时间太长了

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

在这里,罪魁祸首再次是分支预测。
=
测试很少是正确的,因此分支预测对这两个测试的效果大致相同。当您将其更改为
时,您就得到了该问题中解释的行为,原因也是一样的


道德:

我相信“处理”排序数组应该比未排序数组更快


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

比较
=
与排序的关系比
要小。正确或错误地预测
=
只取决于重复值的数量及其分布

您可以使用
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
jason@io/tmp$lz4-d ints | perf stat./proc eq>/dev/null
已成功解码104824717字节
“/proc eq”的性能计数器统计信息:
5226.932577任务时钟(毫秒)#使用了0.953个CPU
31个上下文开关#0.006 K/sec
24次cpu迁移#0.005 K/sec
3479页错误#0.666 K/sec
15763486767个周期#3.016 GHz
4238973549失速循环前端#26.89%前端循环闲置
暂停周期后端
31522072416指令#每周期2.00 INSN
#每insn 0.13次失速循环
8515545178分支机构#1629.167米/秒
10261743个分支未命中#占所有分支的0.12%
5.483071045秒经过的时间
jason@io/tmp$lz4-d ints | sort-n | perf stat./proc eq>/dev/null
已成功解码104824717字节
“/proc eq”的性能计数器统计信息:
5536.031410任务时钟(毫秒)#使用了0.348个CPU
198个上下文开关#0.036 K/sec
21 cpu迁移#0.004 K/秒
3604页错误#0.651 K/sec
16870541124个周期#3.047 GHz
5300218855失速循环前端#31.42%前端循环闲置
暂停周期后端
31526006118指令#每周期1.87 INSN
#每insn 0.17次失速循环
8516336829分支机构#1538.347米/秒
10980571分支机构未命中#占所有分支机构的0.13%
jason@io/tmp$lz4-d ints | perf stat./proc gt>/dev/null
已成功解码104824717字节
“/proc gt”的性能计数器统计信息:
5293.065703任务时钟(毫秒)#使用了0.957个CPU
38个上下文开关#0.007 K/sec
50 cpu迁移#0.009 K/秒
3466页错误#0.655 K/秒
15972451322个周期#3.018 GHz
4350726606失速循环前端#27.24%前端循环怠速
暂停周期后端
31537365299指令#每周期1.97 INSN
#每insn 0.14次失速循环
851560640分支机构#1608.823米/秒
15241198个分支未命中#占所有分支的0.18%
5.532285374秒经过的时间
jason@io/tmp$lz4-d ints | sort-n | perf stat./proc gt>/dev/null
15.930144154秒经过的时间
性能计数器统计数据