Является ли "==" в отсортированном массиве не быстрее, чем несортированный массив?
Примечание: предполагаемый дублирующий вопрос, я думаю, в основном связан с "<" и " > " сравнение, но не сравнение "==" и, следовательно, не отвечает на мой вопрос об эффективности оператора "==" .
Долгое время я считал, что "обработка" отсортированного массива должна быть быстрее, чем несортированный массив. Сначала я подумал, что использование "==" в отсортированном массиве должно быть быстрее, чем в несортированном массиве, потому что, я думаю, о том, как работает предсказание ветвей:
UNSORTEDARRAY:
5 == 100 F
43 == 100 F
100 == 100 T
250 == 100 F
6 == 100 F
(other elements to check)
SORTEDARRAY:
5 == 100 F
6 == 100 F
43 == 100 F
100 == 100 T
(no need to check other elements, so all are F)
поэтому я предполагаю, что SORTEDARRAY должен быть быстрее, чем UNSORTEDARRAY, но сегодня я использовал код для генерации 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();
чтобы проверить, просто подсчитайте, равно ли value == 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);
}
выполните три раза:
UNSORTEDARRAY
0.005376
0.005239
0.005220
SORTEDARRAY
0.005334
0.005120
0.005223
кажется небольшая разница в производительности, поэтому я не поверила, а затем попыталась изменить "SORTEDARRAY [i] == RAND_MAX/2" на "SORTEDARRAY [i] > RAND_MAX/2", чтобы увидеть, разница:
UNSORTEDARRAY
0.008407
0.008363
0.008606
SORTEDARRAY
0.005306
0.005227
0.005146
на этот раз есть большая разница.
Является ли "==" в отсортированном массиве не быстрее, чем несортированный массив? Если да, то почему " > " в отсортированном массиве выполняется быстрее, чем несортированный массив, но "==" не является?
Ответы
Ответ 1
Одна вещь, которая сразу приходит в голову, - это алгоритм прогнозирования ветвления процессора.
В случае сравнения >
в отсортированном массиве поведение ветвления очень непротиворечиво: во-первых, условие if
постоянно ложно, то оно последовательно верно. Это очень хорошо сочетается с простейшим предсказанием ветвей.
В несортированном массиве результат условия >
является, по существу, случайным, что предотвращает любое предсказание ветвления.
Это ускоряет сортировку.
В случае сравнения ==
в большинстве случаев условие ложно и только очень редко оно истинно. Это хорошо работает с предсказанием ветвей независимо от того, отсортирован ли массив или нет. Временные значения по существу одинаковы.
Ответ 2
N.B. Я принимаю этот ответ, потому что он слишком длинный для комментария.
Эффект здесь - это то, что уже подробно объяснено в подробных ответах на этот вопрос. Обработка отсортированного массива в этом случае была быстрее из-за предсказания ветвления.
Здесь преступник снова является прогнозом ветвления. Тест ==
очень редко выполняется, поэтому предсказание ветвлений работает примерно одинаково для обоих. Когда вы изменили его на >
, вы получили объяснение по этому вопросу и по той же причине.
Мораль:
Я считаю, что "обработка" отсортированного массива должна быть быстрее, чем [an] несортированный массив.
Вам нужно знать, почему. Это не какое-то магическое правило, и это не всегда так.
Ответ 3
Сравнение ==
имеет меньшее отношение к порядку, чем >
. Правильное или неправильное прогнозирование ==
будет зависеть только от количества повторяющихся значений и их распределения.
Вы можете использовать perf stat
для просмотра счетчиков производительности...
[email protected] /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
[email protected] /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
[email protected] /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
[email protected] /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