Что более эффективно при компиляции? - PullRequest
2 голосов
/ 19 июля 2011

Я знаю, что в наши дни компьютеры довольно быстрые и с низкими эффективными настройками, такие как я собираюсь спросить, на самом деле не имеют такого большого значения, но я думаю, что это все еще полезно знать.


    int something;
    something = 5;

или


    int something = 5;

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

Ответы [ 6 ]

13 голосов
/ 19 июля 2011

В наши дни, когда вы включаете оптимизацию, вы (в значительной степени) не можете предсказать НИЧЕГО о сгенерированном коде. Верьте или нет, ваш код описывает заканчивается , не средства! Поэтому нет смысла прогнозировать , как будет выполняться, особенно после оптимизации - все, что С гарантирует, - это то, что он даст вам запрошенный результат.

И до оптимизации не имеет смысла беспокоиться об этом.
И ваш код тривиален для оптимизации компилятором, так что не беспокойтесь об этом.

Начните думать о более важных вещах в вашей программе. :)

5 голосов
/ 19 июля 2011

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

3 голосов
/ 20 июля 2011

На самом деле, с опытом вы можете прогнозировать, что будет делать оптимизатор.Esp для чего-то вроде этого.

Я рекомендую вам попробовать самим, чтобы увидеть результаты, нужно около 30 секунд или около того.

Формулировка вашего вопроса не имеет большого смысла, компьютерысами по себе не выполняют настройки эффективности (в связи с тем, что происходит с некоторым кодом C), программы, которые запускаются на этих компьютерах (например, компилятор), создают или не создают эффективность.Поскольку компьютеры продолжают работать быстрее и имеют больше памяти, а программист-человек готов сидеть и ждать компилятора, у компиляторов есть возможность продолжать создавать более эффективные программы.

Вы использовали термин эффективностьа не оптимизация, я полагаю, вам интересно узнать, что оптимизатор будет делать с этим кодом?И вы не указали, какой уровень оптимизации вас заинтересовал, поэтому:

первый случай:

int something = 5;

второй случай:

int something;
...
something = 5;

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

Когда это локальная переменная, это проще.Вы получите то же самое.Без оптимизации он выделит некоторое пространство в стеке и сгенерирует некоторый код для создания и сохранения этой константы в этом месте в стеке.Где бы он ни использовался, он будет загружаться или храниться в этом месте в стеке (в конце концов).Небольшая оптимизация, он все еще может использовать стек, но может не стремиться продолжать использовать эту память в качестве домашней базы для переменной, он может много использовать регистр, и он не будет хранить и загружать обратно везде (никакая оптимизация не является изменчивой, какBTW).При хорошей оптимизации, в зависимости от набора команд цели, в зависимости от того, что компилятор знает о цели (он может знать, что загрузка из оперативной памяти в регистр медленнее, чем немедленная, или может случиться, что использование немедленной памяти хуже или равно) в простейшем случае (переменная получает одно присваивание и находится справа, на стороне операнда знака равенства оттуда и далее), компилятор не обязательно выделит пространство стека или даже не использует регистр, если набор инструкций позволяет, онбудет кодировать непосредственное значение везде, где это возможно.

При использовании в качестве глобальной переменной:

первый случай:

int something = 5;
int main ( void )
{

второй случай:

int something;
int main ( void )
{
    something = 5;

В первом случае будет выделено место в сегменте .data, во втором случае переменная будет выделена из сегмента .bss, нулевого init, сегмента.Это будет вашим первым отличием.

В зависимости от других переменных, от того, как работает операционная система, от формата исполняемого файла и т. Д., Может быть разный объем работы / выполнения ОС.Например, если у вас много других переменных, и это единственная переменная в сегменте .data, то в первом случае потребуется дополнительная работа для чтения сегмента данных в двоичном файле и помещения этих данных в оперативную память.Где, если все ваши переменные были в нулевой инициализации, обнуление одной дополнительной переменной минимально по сравнению со всем кодом, временем и оборудованием для чтения этой дополнительной части двоичного файла.

И наоборот: если все ваши переменные находятся в сегменте .data, за исключением этой переменной в .bss, операционная система требует дополнительной работы для чтения двоичного файла, чтобы выяснить, где и какмного .bss памяти, чтобы очистить.Если все находится в .data, то это чтение и копирование из файла в оперативную память, что не так эффективно, как хранение всего в .bss, но если это был единственный файл в .bss, то заметное снижение производительности по сравнению со всеми переменнымив .data.

При низкой / без оптимизации во втором случае будет сгенерирован код для немедленного использования и записи этой константы в ram, где первый случай не будет таким, каким он уже являлся с момента загрузки двоичного файла,

Когда вы добиваетесь лучшей оптимизации, результат сильно зависит от того, как вы используете эту переменную, сколько кода вокруг нее, насколько широко расположены переменные, если после присваивания переменная когда-либо будет слева равно (получает результат операции против всегда быть операндом). цель вступает в игру и ряд других факторов. вы все еще можете делать довольно хорошие прогнозы относительно того, что произойдет, если вы знаете свой компилятор и цель, но это не всегда будет одинаковым для каждого возможного размера программы или использования этой переменной. Иногда оба случая будут кодировать только непосредственные в инструкциях и никогда не использовать оперативную память или регистр. Иногда оба случая загружают значение в регистр с использованием немедленного и используют этот регистр, даже если переменная получает результат операции. Иногда в течение некоторого времени используется регистр, а затем значение вытесняется в ram, поскольку компилятору требуется больше регистров для реализации следующего сегмента кода, а затем загружает переменную из ram в тот же или какой-то другой регистр для завершения кодирования функция.

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

Лично я использую только второй случай. Во-первых, это чище, более читабельно. Во-вторых, вы получаете недостаток эффективности сегмента .data при загрузке. Но главным образом потому, что я пишу много встроенного кода и пишу свои собственные загрузчики и процедуры запуска. Если вы делаете правило никогда не инициализироваться в объявлении, и вы всегда пишете код для присвоения вашей переменной значения, прежде чем использовать это значение (очень хорошо, что компиляторы теперь предупреждают вас, когда вы используете переменную перед ее назначением, даже если Предполагается, что он равен нулю), тогда в основном у загрузчика никогда нет сегмента .data, что особенно полезно, если у вас заканчивается rom, и во-вторых, загрузчик / запуск никогда не должен обнулять инициализацию какой-либо памяти. Вы можете в конечном итоге записать больше двоичного / флэш-размера / пространства, чем если бы у вас были .data или .bss или оба, и это может стоить вам некоторого времени выполнения. Не всегда, что делает этот трюк / хак, хотя это гораздо более чистый код, гораздо менее рискованный, более надежный, более переносимый, так как вам не нужно вдаваться в нюансы различных линкеров компоновщика (чтобы получить ваш .bss и информация .data, помещаемая для использования кода запуска или загрузчика) (особенно если вы запускаете из flash / rom).

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

0 голосов
/ 19 июля 2011

Как я знаю в оптимизации компилятора, компилятор не резервирует объявленную переменную, если она не определена, поэтому я думаю

int something = 5;

Лучше, чем первый, потому что это уменьшает память и усилия по обработке для компилятора при извлечении переменной "что-то" из

0 голосов
/ 19 июля 2011

в процессе компиляции:

 > Both will contribute the same number of tokens and would mean the same.
 > It would have 2 statements to compile and optimize.
 > Since, in compilation it flows through a state machine, so it would even be difficult to notice the difference.

В процессе исполнения:

> Since, both statements mean the same, during optimization stage of compiler both would become equivalent.
> So they will compile to exactly same assembly code.
> In affect, there won't be any difference at all after obj file is generated.

Итак, эти вещи никогда не имеют значения.

0 голосов
/ 19 июля 2011

Это действительно не должно иметь никакого значения - компилятор перемещает код так сильно, что вы можете даже не распознать свою собственную программу. Тем не менее, почему бы не узнать для себя?

#include <time.h>
#include <stdio.h>

int main ()
{ 
    clock_t start, end;
    double runTime;
    start = clock();

    int i;
    for (i=0; i<10000000; i++}
    {
         int something;
         something = 5;             
    }

    end = clock();
    runTime = (end - start) / (double) CLOCKS_PER_SEC ;
    printf ("Run time one is &#37;g seconds\n", runTime);

    clock_t start, end;
    double runTime;
    start = clock();

    int i;
    for (i=0; i<10000000; i++}
    {
         int something = 5;             
    }

    end = clock();
    runTime = (end - start) / (double) CLOCKS_PER_SEC ;
    printf ("Run time two is &#37;g seconds\n", runTime);
    getchar();
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...