Я оптимизирую или позволю компилятору, чтобы сделать это?

Что является предпочтительным методом записи циклов согласно эффективности: Путь a)

   /*here I'm hoping that compiler will optimize this  
 code and won't be calling size every time it iterates through this loop*/
    for (unsigned i = firstString.size(); i < anotherString.size(), ++i)
    {
    //do something
    }

или возможно должен я делать это этот путь: Путь b)

unsigned first = firstString.size();
unsigned second = anotherString.size();

и теперь я могу записать:

    for (unsigned i = first; i < second, ++i)
    {
    //do something
    }

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

25
задан foraidt 17 June 2010 в 09:33
поделиться

13 ответов

Обычно я пишу этот код так:

/* i and size are local to the loop */
for (size_t i = firstString.size(), size = anotherString.size(); i < size; ++i) {
  //do something
}

таким образом я не загрязняю родительскую область видимости и избегаю вызова anotherString.size() для каждой итерации цикла.

это особенно полезно для итераторов:

for(some_generic_type<T>::forward_iterator it = collection.begin(), end = collection.end();
    it != end; ++it) {
   // do something with *it
}
62
ответ дан 28 November 2019 в 17:37
поделиться

Вот как я на это смотрю. Качество и стиль важны, и вам придется выбирать между ними.

Вы можете попробовать это и посмотреть, снизится ли производительность. Если есть недопустимый удар по производительности, выберите второй вариант, в противном случае смело выбирайте стиль.

1
ответ дан 28 November 2019 в 17:37
поделиться

Нет ничего плохого в способе (b), если вы просто хотите написать что-то, что, вероятно, будет не хуже, чем способ (a), а возможно и быстрее. Это также проясняет, что вы знаете, что размер строки останется постоянным.

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

(Также первая переменная в стиле (b) не нужна; код для выражения инициализации запускается только один раз.)

1
ответ дан 28 November 2019 в 17:37
поделиться

Сначала я бы использовал первую версию просто потому, что она выглядит чище и легче набирается. Затем вы можете профилировать его, чтобы увидеть, нужно ли что-то еще оптимизировать.

Но я очень сомневаюсь, что первая версия вызовет заметное падение производительности. Если контейнер реализует size () следующим образом:

inline size_t size() const
{
    return _internal_data_member_representing_size;
}

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

15
ответ дан 28 November 2019 в 17:37
поделиться

Это одна из тех вещей, которые вы должны проверить сами. Выполните 10 000 или даже 100 000 итераций циклов и посмотрите, какая разница существует.

Это должно сказать вам все, что вы хотите знать.

3
ответ дан 28 November 2019 в 17:37
поделиться

Я рекомендую позволить несущественным оптимизациям проникнуть в ваш стиль. Я имею в виду, что если вы научитесь более оптимальному способу выполнения чего-либо и не видите в нем каких-либо недостатков (в том, что касается ремонтопригодности, удобочитаемости и т. Д.), Вы также можете принять его.

Но не становитесь одержимыми. Оптимизации, которые приносят в жертву ремонтопригодности, должны быть сохранены для очень небольших участков кода, которые вы измерили, и ЗНАТЬ окажет большое влияние на ваше приложение. Когда вы все же решите оптимизировать, помните, что выбор правильного алгоритма для работы часто гораздо важнее, чем жесткий код.

3
ответ дан 28 November 2019 в 17:37
поделиться

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

Вы можете потратить часы, пытаясь улучшить один цикл, только чтобы получить прирост производительности на 0,001%. Если вас беспокоит производительность - используйте профилировщики.

1
ответ дан 28 November 2019 в 17:37
поделиться

Не оптимизируйте ваш код заранее. Если у вас есть проблемы с производительностью, используйте профилировщик, чтобы найти их, иначе вы тратите время на разработку. Просто напишите максимально простой / чистый код.

6
ответ дан 28 November 2019 в 17:37
поделиться

Я надеюсь, что компилятор оптимизирует это ...

Не стоит. Компилятору C ++ сложно оптимизировать все, что связано с вызовом

  • неизвестной функции или
  • метода, который может быть переопределен

. Вам может повезти, но вы не можете рассчитывать на это.

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

2
ответ дан 28 November 2019 в 17:37
поделиться
  1. Сколько процентов времени тратится на для по сравнению с // что-то делать ? (Не угадайте - попробуйте.) Если он <10%, у вас, вероятно, есть более серьезные проблемы в другом месте.

  2. Все говорят: «В наши дни компиляторы такие умные». Что ж, они не умнее бедных программистов, которые их пишут. Вам тоже нужно быть умным. Может быть, компилятор сможет его оптимизировать , но почему бы его не соблазнить?

1
ответ дан 28 November 2019 в 17:37
поделиться

Для функции-члена «std :: size_t size () const», которая не только имеет значение O (1), но также объявлена ​​«const» и поэтому может быть автоматически выведена из цикла с помощью компилятор, это, вероятно, не имеет значения.Тем не менее, я бы не стал рассчитывать на то, что компилятор удалит его из цикла, и я думаю, что это хорошая привычка выделять вызовы внутри цикла для случаев, когда функция не является постоянной или O (1 ). Кроме того, я думаю, что присвоение значений переменной делает код более читабельным. Однако я бы не предлагал вам делать какие-либо преждевременные оптимизации, если это приведет к тому, что код будет труднее читать. Опять же, я считаю, что следующий код более читабелен, поскольку в цикле меньше читается:

 std::size_t firststrlen = firststr.size();
 std::size_t secondstrlen = secondstr.size();
 for ( std::size_t i = firststrlen; i < secondstrlen; i++ ){
      // ...
 }

Кроме того, я должен указать, что вы должны использовать «std :: size_t» вместо «unsigned», поскольку тип «std :: size_t» может варьироваться от одной платформы к другой, а использование «беззнакового» может привести к усечению и ошибкам на платформах, для которых тип «std :: size_t» имеет значение «unsigned long» вместо «unsigned int» ".

0
ответ дан 28 November 2019 в 17:37
поделиться

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

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

17
ответ дан 28 November 2019 в 17:37
поделиться

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

Такой вид оптимизации небезопасен с точки зрения компилятора, вам нужно делать это самостоятельно. Самостоятельная работа не означает, что вам нужно ввести две дополнительные локальные переменные. В зависимости от вашей реализации размера это может быть операция O (1). Если size также объявлен встроенным, вы также избавитесь от вызова функции, сделав вызов size () таким же эффективным, как и доступ к локальному члену.

7
ответ дан 28 November 2019 в 17:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: