2016-08-08 4 views
2

Я написал этот базовый код для DSP/аудиоприложения Делаю:Объявить, что внешний внешний цикл плохо?

double input = 0.0; 
for (int i = 0; i < nChannels; i++) { 
     input = inputs[i]; 

и некоторого DSP инженерного эксперта сказать мне: «вы не должны объявить его вне цикла, в противном случае создать и компилятор не может справиться с этим как можно более эффективно ».

Он говорит о var input Я думаю. Почему это? Не лучше ли decleare один раз и перезаписать его?

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

+5

В этом конкретном примере кода «двойной вход = входы [nChannels -1];' было бы еще лучше. – StoryTeller

+7

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

+1

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

ответ

3

Многие люди думают, что объявление переменной выделяет некоторую память для вас. Это не работает. Он также не выделяет реестр.

Это только создает для вас имя (и связанный с ним тип), который вы можете использовать для связи потребителей ценностей со своими производителями.

На 50-летнем компиляторе (или написанном студентами на третьем курсе Compiler Construction), который может быть реализован путем выделения некоторой памяти для переменной в стеке и использования этого при каждой ссылке на переменную , Это просто, это работает, и это ужасно неэффективно. Хороший шаг заключается в том, что локальные переменные помещаются в регистры, когда это возможно, но они неэффективно используют регистры, и это не то место, где мы сейчас находимся (были в течение некоторого времени).

Связывание потребителей с производителями создает график потока данных. В большинстве современных компиляторов это ребра на этом графе, которые получают регистры. Это полностью удаляется из любых переменных, как вы их объявили. Их больше нет. Вы можете увидеть это в действии, если вы используете -emit-llvm в clang.

Так что переменные не являются реальными, они просто ярлыки. Используйте их, как хотите.

+0

Я думаю, что объявление переменных в наименьшей возможной области считается хорошим стилем. Это улучшает удобочитаемость и может защищаться от ошибок при изменении кода позже. Но да, это только проблема стиля для современных компиляторов с оптимизацией. Я слышал, что компиляторы, поставляемые поставщиком для некоторых встроенных процессоров, не очень хороши, и один поставщик бесплатно предоставляет оптимизационный компилятор, но вам нужно заплатить за версию, которая позволяет включить оптимизацию. Таким образом, люди с фоном DSP могут использоваться для плохих компиляторов? –

3

Лучше объявить переменную внутри цикла, но причина неверна.

Существует правило: объявить переменные в наименьшей возможной области. Ваш код более читабельен и меньше подвержен ошибкам.

Что касается вопроса о производительности, это не имеет никакого значения для любого современного компилятора, где именно вы объявляете свои переменные. Например, clang устраняет переменную полностью на -O1 от своего собственного ИК: https://godbolt.org/g/yjs4dA

Один угол дела, однако: если вы когда-либо принимает адрес input, переменная can't be eliminated (легко), и вы должны объявить его внутри цикла, если вы заботитесь о производительности.

+0

», и вы должны объявить его внутри петля ", почему это лучше? – markzzz

+0

@paizza, посмотрите пример [здесь] (https://godbolt.org/g/dZZMTH). В 'foo_bad' под меткой' .lr.ph' (тело цикла) есть два 'load', а в' foo_good' - только один. Принимая адрес переменной, вводит сложную зависимость, а компилятор больше не может устранить эту переменную (и 'load' /' store'). – deniss

+1

Это не просто адрес, вот в чем проблема; это позволяет этому адресу избежать того, что оптимизатор может видеть, что побеждает оптимизация. Кроме того, ваши хорошие и плохие функции скомпилируются с эквивалентом x86-64 asm (удалив опцию '-emit-llvm' из URL-адреса god-got -O2 в вашем комментарии. Я не смотрел на LLVM IR.) Поскольку' g' может изменить 'input [i]', компилятор должен разлить/перезагрузить 'input_internal' через вызов' g'. Вы получаете больше [эффективного кода от передачи «входов [i]» в качестве аргумента для обеих функций] (https://godbolt.org/g/Y0sSB3). –

7

Хорошо старые K & Компиляторы R C в начале восьмидесятых используются для создания кода как можно ближе к тому, что написал программист, а программисты использовали все возможное для создания оптимизированного исходного кода. Современные оптимизирующие компиляторы могут перерабатывать вещи при условии, что полученный код имеет одинаковые наблюдаемые эффекты в качестве исходного кода. Поэтому здесь, если переменная input не используется вне цикла, оптимизирующий компилятор может оптимизировать линию double input = 0.0;, потому что нет никаких наблюдаемых эффектов до следующего назначения: input = inputs[i];. И по той же причине может быть тот же фактор, что и перенос переменной вне цикла (будь то в исходном файле C++ внутри или нет).

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

Важно то, что это просто читаемость и переменная область видимости. Здесь input является функционально локальным для цикла, поэтому он должен быть объявлен внутри цикла. Полная остановка. Любые другие соображения оптимизации просто бесполезны, если у вас нет особых требований к оптимизации низкого уровня (профилирование, показывающее, что эти строки требуют специальной обработки).

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

  • Нет связанных вопросов^_^