Является ли == в отсортированном массиве не быстрее, чем несортированный массив?

Примечание: предполагаемый повторяющийся вопрос, я думаю, в основном связан со сравнением "‹" и ">", но не со сравнением "==" и, следовательно, не отвечает на мой вопрос о производительности оператора "==".

Долгое время я считал, что «обработка» отсортированного массива должна быть быстрее, чем несортированного массива. Сначала я подумал, что использование "==" в отсортированном массиве должно быть быстрее, чем в несортированном массиве, потому что, я думаю, как работает предсказание ветвления:

НЕСОРТИРОВКА:

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

SortedRearay:

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

поэтому для проверки просто посчитайте, равно ли значение == 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

SORTEDARRAY

0.005334
0.005120
0.005223

это кажется небольшой разницей в производительности, поэтому я не поверил, а затем попытался изменить «SORTEDARRAY[i]==RAND_MAX/2» на «SORTEDARRAY[i]>RAND_MAX/2», чтобы увидеть, имеет ли это значение:

НЕСОРТИРОВКА

0.008407
0.008363
0.008606

SORTEDARRAY

0.005306
0.005227
0.005146

на этот раз есть большая разница.

Является ли "==" в отсортированном массиве не быстрее, чем несортированный массив? Если да, то почему «>» в ​​отсортированном массиве быстрее, чем несортированный массив, а «==» — нет?


person ggrr    schedule 18.08.2015    source источник
comment
Относится к одному из самых популярных вопросов всех времен: stackoverflow.com/questions/11227809/   -  person Andrzej Pronobis    schedule 18.08.2015
comment
Я считаю, что обработка отсортированного массива должна быть быстрее, чем несортированного массива: попробуйте ответить себе, почему вы думаете, что это верно для этого алгоритма. То есть - какую работу и в каком объеме вы делаете для каждого конкретного случая. Вы можете понять, каков ответ.   -  person viraptor    schedule 18.08.2015
comment
string не является стандартным типом в C, и использование оператора += с одним операндом типа string, а другим char * не имеет смысла. Вы уверены, что это не код C++?   -  person autistic    schedule 18.08.2015
comment
Кроме того, что вы используете для синхронизации этого кода? Что-то очень неточное и, вероятно, предвзятое. Такие вопросы обычно пишут дезинформированные люди У вас вообще включена полная оптимизация? У вас есть реальная проблема, которую нужно решить, и программа для решения этой проблемы? Используете ли вы профилировщик в этой программе, чтобы определить существенные узкие места? Причина, по которой я спрашиваю, заключается в том, что в любом реалистичном сценарии узкие места будут значительно отличаться от того, что вы описали. Этот вопрос не имеет практического значения.   -  person autistic    schedule 18.08.2015
comment
Мы даже не знаем, какой компилятор вы используете! Возможно, предвзятость, которую вы ввели в свой полный mcve (которого у нас также нет), зависит от используемого вами компилятора, и система, для которой вы программируете? Этот вопрос слишком широк, поэтому я голосую за его закрытие.   -  person autistic    schedule 18.08.2015
comment
Почему вы предполагаете (нет необходимости проверять другие элементы, поэтому все они F)? Компилятор не может этого знать, он просто будет слепо проверять каждую ячейку памяти. Действительно, при использовании случайных данных оно лишь в редких случаях будет равно фиксированному значению, поэтому его очень легко предсказать ЦП.   -  person Mihai Timar    schedule 23.02.2017
comment
См. stackoverflow.com/questions/11227809/.   -  person thegoodhunter-9115    schedule 30.11.2020


Ответы (3)


Одна вещь, которая сразу приходит на ум, — это алгоритм предсказания ветвлений ЦП.

В случае сравнения > в отсортированном массиве поведение ветвления очень последовательное: сначала условие if неизменно ложно, затем оно неизменно истинно. Это очень хорошо согласуется даже с простейшим прогнозированием ветвлений.

В несортированном массиве результат условия > по существу является случайным, что препятствует любому предсказанию ветвления.

Это то, что делает отсортированную версию быстрее.

В случае сравнения == большую часть времени условие ложно и лишь очень редко верно. Это хорошо работает с прогнозированием ветвлений независимо от того, отсортирован ли массив или нет. Сроки по сути одинаковые.

person AnT    schedule 18.08.2015

Н.Б. Я делаю этот ответ, так как он слишком длинный для комментария.

Эффект здесь именно тот, который уже подробно объяснен в многочисленных ответах на этот вопрос. В этом случае обработка отсортированного массива была быстрее из-за предсказания ветвлений.

Здесь виновником снова является предсказание ветвления. Тест == очень редко оказывается верным, поэтому предсказание ветвлений работает примерно одинаково для обоих. Когда вы изменили его на >, вы получили объяснение поведения в этом вопросе по той же причине.


Мораль:

Я считаю, что «обработка» отсортированного массива должна быть быстрее, чем [] несортированный массив.

Вы должны знать почему. Это не какое-то волшебное правило, и оно не всегда верно.

person imallett    schedule 18.08.2015

Сравнение == имеет меньшее отношение к порядку, чем >. Правильное или неправильное предсказание == будет зависеть только от количества повторяющихся значений и их распределения.

Вы можете использовать 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
person Jason    schedule 18.08.2015