#c
#c
Вопрос:
Что более эффективно? Или они оба одинаково эффективны? Что происходит в базовой архитектуре в выделенных строках?
(1)
for(int i = m_size; i > index; --i)
{
int k = normalize(i); ***
m_data[k] = m_data[k - 1];
}
или (2)
int k = 0; ***
for(int i = m_size; i > index; --i)
{
k = normalize(i); ***
m_data[k] = m_data[k - 1];
}
Комментарии:
1. Вы можете
cc -S file.c
проверить сгенерированную сборку («базовая архитектура»). Но если вы не пишете игровые движки или драйверы устройств, вам обычно следует больше беспокоиться о удобочитаемости, чем об эффективности.2. Потрясающе! Я понятия не имел, что вы могли бы это сделать!
3.
k
не нужно инициализировать во втором примере. После удаления этого любой разумный компилятор сгенерирует идентичный код для обоих. В частности, выделение и освобождениеk
внутри цикла не требует каких-либо машинных инструкций: фрейм стека определяется для всего метода при вводе.
Ответ №1:
фрагмент # 1 более эффективен для наивного компилятора, потому что второй фрагмент требует загрузки 0 в переменную k.
Хороший компилятор распознает, что k не используется до загрузки k в цикл for, и оптимизирует дополнительное назначение.
Вы можете использовать сниппет 2 без загрузки 0: int k;
Комментарии:
1. Не будет ли разницы в эффективности в хорошем компиляторе, если я удалю инициализацию?
2. Вам нужно было бы посмотреть на ассемблер. некоторые компиляторы поймали бы это, другие — нет. Я бы объявил int k над циклом без инициализации, но компилятору должно быть все равно.
3. Но теоретически, во фрагменте 1, разве он не воссоздает int на каждой итерации, поскольку k технически выходит за рамки в конце цикла? Конечно, компилятор мог бы оптимизировать это, но я подумал, что теоретически было бы лучше сделать фрагмент 2 из-за этого.
4. @Chipster: Я согласен, что вы можете смотреть на это таким образом.
Ответ №2:
Не беспокойтесь о такой оптимизации на микроуровне. Пусть компилятор / оптимизатор сделает это за вас. Согласно стилю C , 2-й подход лучше, поскольку он объявляет переменную, близкую к использованию (как обсуждается в книге «Code Complete»). Кроме стиля C, первый подход хорош, если вы хотите использовать переменную после цикла.
Обратите внимание, что функция normalize
, тип m_data
, []
задействованный оператор и т. Д. все играют роль в оптимизации. Размер данных, параллелизм, архитектура процессора также играют важную роль в производительности программы во время выполнения.
Ответ №3:
Трудно сказать, что «происходит в базовой архитектуре», не зная, о какой архитектуре вы говорите. Но в целом на любой вопрос типа «что более эффективно» можно ответить только очевидным способом: путем измерения, какая версия в конечном итоге оказывается более эффективной в вашем конкретном приложении…
Обратите внимание, что любой удаленный полезный компилятор должен превратить оба ваших примера в один и тот же машинный код…
Ответ №4:
Это не проблема эффективности. Первый подход лучше, потому что он использует RAII (выделение ресурсов — это инициализация). Второй вариант больше похож на C. Вы должны объявлять переменные при его использовании, а не в начале функционального блока. Смотрите этот пример:
void f1(bool use)
{
ComplexObject c; // creating an object that has very costly, time-and-memory-consuming initialization
if (! use)
return;
c.someFunction();
}
Понимаете, что я имею в виду?