Я пытаюсь понять, как работает модуль предсказания ветвления в CPU.
Я использовал papi
, а также linux * perf-events
, но оба они не дают точных результатов (для моего случая).
Это мой код:
void func(int* arr, int sequence_len){
for(int i = 0; i < sequence_len; i++){
// region starts
if(arr[i]){
do_sth();
}
// region ends
}
}
Мой массив состоит из 0 и 1. Имеет шаблон размером sequence_len
. Например, если мой размер равен 8, то он имеет шаблон 0 1 0 1 0 0 1 1
или что-то в этом роде.
Испытание 1:
Я пытаюсь понять, как CPU предсказывает эти ветви. Итак, я использовал papi и настроил счетчик производительности для предсказанных ветвлений прогнозов (я знаю, что он также подсчитывает косвенные ветки).
int func(){
papi_read(r1);
for(){
//... same as above
}
papi_read(r2);
return r2-r1;
}
int main(){
init_papi();
for(int i = 0; i < 10; i++)
res[i] = func();
print(res[i]);
}
Что я вижу в качестве вывода (для длины последовательности 200)
100 #iter1
40 #iter2
10 #iter3
3
0
0
#...
Итак, сначала ЦП вслепую предсказывает последовательность, только успех в половине случаев. На следующих итерациях процессор может прогнозировать все лучше и лучше. После некоторого количества итераций процессор может догадаться, что это прекрасно.
Пробная версия 2
Я хотел бы увидеть, при каком индексе массива ошибочно прогнозируется процессор.
int* func(){
int* results;
for(){
papi_read(r1);
if(arr[i])
do_sth();
papi_read(r2);
res[i] = r2-r1;
}
return res;
}
int main(){
init_papi();
for(int i = 0; i < 10; i++)
res[i] = func();
print(res[i]);
}
Ожидаемый результат:
#1st iteration, 0 means no mispred, 1 means mispred
1 0 0 1 1 0 0 0 1 1 0... # total of 200 results
Mispred: 100/200
#2nd iteration
0 0 0 0 1 0 0 0 1 0 0... # total of 200 results
Mispred: 40/200 # it learned from previous iteration
#3rd iteration
0 0 0 0 0 0 0 0 1 0 0... # total of 200 results
Mispred: 10/200 # continues to learn
#...
Полученный результат:
#1st iteration
1 0 0 1 1 0 0 0 1 1 0... # total of 200 results
Mispred: 100/200
#2nd iteration
1 0 0 0 1 1 0 1 0 0 0... # total of 200 results
Mispred: 100/200 # it DID NOT learn from previous iteration
#3rd iteration
0 1 0 1 0 1 0 1 1 0 0... # total of 200 results
Mispred: 100/200 # NO LEARNING
#...
Мои наблюдения
Когда я измеряю неверное прогнозирование кроме как для l oop, я вижу, что процессор учится на своих неправильных предсказаниях. Однако, когда я пытаюсь измерить неправильное прогнозирование инструкций для одной ветви, ЦП либо не может учиться, либо я измеряю это неправильно.
Мое объяснение
Я даю 200 как длина последовательности. Процессор имеет один небольшой предиктор ветвления, такой как 2-3-битный насыщенный счетчик в Intel, и один большой глобальный предиктор ветвления. Когда я измеряю за пределами l oop, я добавляю меньше шума к измерению. Под меньшим шумом я имею в виду papi
звонки.
Подумайте об этом: за пределами l oop измерения
глобальная история: papi_start, branch_outcome1, branch_outcome2, branch_outcome3, ..., papi_end, papi_start (2nd loop of main iteration), branch_outcome1, ...
Итак предиктор ветви каким-то образом находит шаблон в той же ветви.
Однако, если я попытаюсь измерить инструкцию с одной ветвью, глобальная история будет выглядеть так: papi_start, branchoutcome1, papiend, papistart, branchoutcome2, papiend...
Итак, я представляю больше и больше веток в мировой истории. Я предполагаю, что глобальная история не может содержать много записей ветвления и, следовательно, не может найти какую-либо корреляцию / шаблон в требуемом операторе if (ветвь).
В результате
Мне нужно измерить результат предсказания одной ветви. Я знаю, что процессор может выучить шаблон 200, если я не буду слишком много вводить папи. Я посмотрел на вызовы папи и видел множество циклов for, если условия.
Вот почему мне нужно лучшее измерение. Я пробовал linux perf-event
, но он делает ioctl
вызовы, что является системным вызовом, и я загрязняю глобальную историю системными вызовами, и, следовательно, это не очень хорошее измерение.
Я читал, что rdpmc
и rdmsr
инструкции, и я предполагаю, что, поскольку они являются только инструкциями, я не буду загрязнять глобальную историю, и я могу измерять одну инструкцию за один раз.
Однако я понятия не имею, как я могу это сделать У меня процессор AMD 3600. Это ссылки, которые я нашел в Интернете, но я не мог понять, как это сделать. В дополнение к этому, я что-то упустил?
Intel rdpm c
Руководство по производительности AMD