Проблема:
Я пытаюсь понять, как написать код (C preffered, ASM, только если нет другого решения), который сделает прогностическое отклонение ветвления в 50% случаев.
Таким образом, это должна быть часть кода, которая "является imune" для оптимизаций компилятора, связанных с ветвлением, а также все предсказания ветвления HW не должны превышать 50% (бросая монету). Еще большая проблема заключается в том, чтобы запустить код на архитектуре нескольких процессоров и получить то же 50% -ное отклонение.
Мне удалось написать код, который идет на 47% -ный коэффициент пропускания ветвей на платформе x86. Я подозреваю, что пропавшие могут получить 3%:
- Накладные расходы на запуск программы, которые имеют ветвление в ней (очень маленький)
- Накладные расходы профилировщика - в основном для каждого счетчика считывается прерывание, поэтому это может добавить дополнительные предсказуемые ветки.
- Системные вызовы, выполняющиеся в фоновом режиме, содержащие циклы и предсказуемое ветвление
Я написал свой собственный генератор случайных чисел, чтобы избежать обращения к rand, реализация которого может иметь скрытые предсказуемые ветки. Он также может использовать rdrand, когда он доступен. Задержка не имеет значения для меня.
Вопросы:
- Могу ли я сделать лучше, чем моя версия кода? Лучше означает получение более раннего отклонения от ветки и одинаковые результаты для всех архитектур процессора.
- Может ли этот код использоваться? Что это значит?
Код:
#include <stdio.h>
#include <time.h>
#define RDRAND
#define LCG_A 1103515245
#define LCG_C 22345
#define LCG_M 2147483648
#define ULL64 unsigned long long
ULL64 generated;
ULL64 rand_lcg(ULL64 seed)
{
#ifdef RDRAND
ULL64 result = 0;
asm volatile ("rdrand %0;" : "=r" (result));
return result;
#else
return (LCG_A * seed + LCG_C) % LCG_M;
#endif
}
ULL64 rand_rec1()
{
generated = rand_lcg(generated) % 1024;
if (generated < 512)
return generated;
else return rand_rec1();
}
ULL64 rand_rec2()
{
generated = rand_lcg(generated) % 1024;
if (!(generated >= 512))
return generated;
else return rand_rec2();
}
#define BROP(num, sum) \
num = rand_lcg(generated); \
asm volatile("": : :"memory"); \
if (num % 2) \
sum += rand_rec1(); \
else \
sum -= rand_rec2();
#define BROP5(num, sum) BROP(num, sum) BROP(num, sum) BROP(num, sum) BROP(num, sum) BROP(num, sum)
#define BROP25(num, sum) BROP5(num, sum) BROP5(num, sum) BROP5(num, sum) BROP5(num, sum) BROP5(num, sum)
#define BROP100(num, sum) BROP25(num, sum) BROP25(num, sum) BROP25(num, sum) BROP25(num, sum)
int main()
{
int i = 0;
int iterations = 500000;
ULL64 num = 0;
ULL64 sum = 0;
generated = rand_lcg(0) % 54321;
for (i = 0; i < iterations; i++)
{
BROP100(num, sum);
// ... repeat the line above 10 times
}
printf("Sum = %llu\n", sum);
}
Обновить v1:
Следуя предложению usr, я сгенерировал различные шаблоны, изменив параметр LCG_C из командной строки в script. Я смог перейти на прохождение BP на 49,67%. Этого достаточно для моей цели, и у меня есть методология для создания этого на разных архитектурах.