Реально ли использовать -O3 или -Ofast для компиляции кода теста или удалить код?

При компиляции кода теста ниже с -O3 Я был впечатлен разницей в задержке, поэтому я начал задаваться вопросом, а не «обманывает» ли компилятор, удаляя код каким-либо образом. Есть ли способ проверить это? Я в безопасности для сравнения с -O3? Реально ли ожидать 15-кратного увеличения скорости?

Результаты без -O3: Средний: 239 Нанос Мин: 230 нанос (9 миллионов итераций)
Результаты с-O3: Средний: 14 нанос, мин: 12 нанос (9 миллионов итераций)

int iterations = stoi(argv[1]);
int load = stoi(argv[2]);

long long x = 0;

for(int i = 0; i < iterations; i++) {

long start = get_nano_ts(); // START clock

for(int j = 0; j < load; j++) {
if (i % 4 == 0) {
x += (i % 4) * (i % 8);
} else {
x -= (i % 16) * (i % 32);
}
}

long end = get_nano_ts(); // STOP clock

// (omitted for clarity)
}

cout << "My result: " << x << endl;

Примечание: я использую clock_gettime измерять:

long get_nano_ts() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000000 + ts.tv_nsec;
}

4

Решение

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

for (int j = 0;  j < load;  ++j)
if (i % 4 == 0)
x += (i % 4) * (i % 8);
else    x -= (i % 16) * (i % 32);

Проницательный компилятор может увидеть это и изменить код на что-то вроде:

 x = load * 174;   // example only

Я знаю, что это не эквивалентно, но есть довольно простое выражение, которое может заменить этот цикл.

Чтобы быть уверенным, стоит использовать gcc -S Опция компилятора и посмотрите на код сборки, который он генерирует.

1

Другие решения

Компилятор, безусловно, будет «обманывать» и удалять ненужный код при компиляции с включенной оптимизацией. На самом деле он идет очень долго, чтобы ускорить ваш код, что почти всегда приводит к впечатляющим ускорениям. Если бы он каким-то образом смог вывести формулу, которая вычисляет результат за постоянное время, вместо использования этого цикла, он бы это сделал. Постоянный коэффициент 15 не является чем-то необычным.

Но это делает не означает, что вы должны профилировать неоптимизированные сборки! Действительно, при использовании таких языков, как C и C ++, производительность неоптимизированных сборок практически не имеет смысла. Вам не нужно беспокоиться об этом вообще.

Конечно, это может помешать микро-тестам, как показано выше. Два момента к этому:

  1. Чаще всего такая микрооптимизация тоже не имеет значения. Предпочитаю профилирование вашего фактический запрограммировать и затем устранить узкие места.
  2. Если вы действительно хотите такой микро-тест, сделайте так, чтобы он зависел от некоторого ввода во время выполнения и отобразил результат. Таким образом, компилятор не может удалить саму функциональность, просто сделать это достаточно быстро.

Поскольку вы, кажется, делаете это, у кода, который вы показываете, есть хорошие шансы стать разумным микро-эталоном. Одна вещь, которую вы должны остерегаться, это то, что ваш компилятор перемещает оба вызова get_nano_ts(); на той же стороне петли. Это разрешено делать, так как «время выполнения» не считается видимым побочным эффектом. (Стандарт даже не предписывает вашей машине работать на конечной скорости.) Утверждалось Вот что это обычно не проблема, хотя я не могу судить, является ли данный ответ верным или нет.

Если ваша программа не делает ничего дорогого, кроме того, что вы хотите сравнить (что она, по возможности, не должна делать в любом случае), вы также можете переместить измерение времени «за пределы», например. с время.

2

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

Один из способов сделать это — распечатать результаты расчетов после остановки таймера:

long long x = 0;

for(int i = 0; i < iterations; i++) {

long start = get_nano_ts(); // START clock

for(int j = 0; j < load; j++) {
if (i % 4 == 0) {
x += (i % 4) * (i % 8);
} else {
x -= (i % 16) * (i % 32);
}
}

long end = get_nano_ts(); // STOP clock

// now print out x so the compiler doesn't just ignore it:
std::cout << "check: " << x << '\n',

// (omitted for clarity)
}

При сравнении тестов для нескольких разных алгоритмов это также может служить проверкой того, что каждый алгоритм выдает одинаковые результаты.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector