2015-09-29 2 views
4

Я создал эту программу. Он не представляет интереса, но использует вычислительную мощность.Почему мой компилятор C++ не оптимизирует эту память?

Глядя на вывод с objdump -d, я вижу три звонка rand и соответствующие инструкции mov в конце, даже при компиляции с O3.

Почему компилятор не понимает, что память не будет использоваться, и просто замените нижнюю половину на while(1){}? Я использую gcc, но меня больше всего интересует то, что требуется по стандарту.

/* 
* Create a program that does nothing except slow down the computer. 
*/ 
#include <cstdlib> 
#include <unistd.h> 

int getRand(int max) { 
    return rand() % max; 
} 

int main() { 
    for (int thread = 0; thread < 5; thread++) { 
    fork(); 
    } 
    int len = 1000; 
    int *garbage = (int*)malloc(sizeof(int)*len); 
    for (int x = 0; x < len; x++) { 
    garbage[x] = x; 
    } 
    while (true) { 
    garbage[getRand(len)] = garbage[getRand(len)] - garbage[getRand(len)]; 
    } 
} 
+0

Какие флаги вы указываете gcc? –

+0

'g ++ -O3 slowdown.cc' - это полная команда, которую я использую. –

+0

Я думаю, вы могли бы утверждать, что получение другой псевдослучайной последовательности изменяет наблюдаемое поведение программы - если программа фактически выводит результат более позднего «rand()», который этого не делает. –

ответ

10

Поскольку GCC недостаточно умен, чтобы выполнять эту оптимизацию на динамически распределенной памяти. Однако, если вы измените garbage быть локальным массивом вместо GCC компилирует петлю на это:

.L4: 
    call rand 
    call rand 
    call rand 
    jmp .L4 

Это просто вызывает rand повторно (который необходим, потому что вызов имеет побочные эффекты), но оптимизирует из чтений и пишет.

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

+0

Nice наблюдения. Я попробовал это на [godbolt] (https://goo.gl/bnzOg3) и обнаружил, что только gcc, not clang или icc может оптимизировать доступ к массиву (и модулю 1000 на выходе из rand () '). Кроме того, gcc может определить его только с помощью 'int garbage [1000]', а не 'int garbage [len]', хотя 'len', как известно, является константой времени компиляции. Даже с '-fwhole-program', все, что gcc-выигрыш не испускает не-встроенное определение для' getRand() '. то есть он может обрабатывать его, как если бы он был объявлен 'static' или' inline'. –

5

Он не может, в общем, сказать, что rand() не наблюдаемые побочные эффекты здесь и не требуется, чтобы удалить эти вызовы.

Он может удалить записи, но может быть использование массивов достаточно, чтобы подавить это.

Стандарт не требует и не запрещает то, что он делает. Пока программа имеет правильное наблюдаемое поведение, любая оптимизация - это чисто качество реализации.

+0

Таким образом, он может заменить конечный цикл на 'while (true) {rand();}'. но не так ли? –

+0

Да, вот что я предлагаю. –

+1

'rand()' является стандартной библиотечной функцией, поэтому компилятор точно знает, что она делает (стандарт определяет ее). –

3

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

В C++ 14 текст 1,10/27:

Реализация может предположить, что любой поток, в конечном счете сделать одно из следующих действий:

  • кончить,
  • сделать вызов функции ввода-вывода библиотеки,
  • доступ или изменение изменчивого объекта, или
  • выполнить операцию синхронизации или атомную опера Тион.

[Примечание. Это предназначено для разрешения преобразований компилятора, таких как удаление пустых петель, даже если завершение невозможно доказать. -end note]

Я бы не сказал, что rand() считается функцией ввода-вывода.

Related question

+0

Теоретически это могло быть правдой (хотя вряд ли это будет причиной здесь). Но здесь вы можете легко изменить цикл, чтобы быть конечным, и видеть, что результат тот же. – interjay

+0

@interjay, если OP обновляет код, чтобы не иметь бесконечного цикла и все еще показывает проблему, я удалю этот ответ –

+0

Я не думаю, что такой педантичный ответ, как это, здесь используется. Кроме того, я думаю, что реалистично любой компилятор будет допускать бесконечные циклы, сколько у них будет таких программ. – interjay

0

Оставьте это шанс врезаться переполнением массива! Компилятор не будет размышлять о диапазоне выходов getRand.

+0

Почему бы и нет? Это по модулю означает, что мы знаем его 0 <= getRand

+0

'rand' возвращает' int', поэтому modulo может быть отрицательным, и вам нужно копать глубже! Я не считаю, что какой-нибудь оптимизатор будет играть в эту игру. –

Смежные вопросы