Что из следующего будет более эффективным? - PullRequest
2 голосов
/ 08 октября 2010

В С:

Допустим, функция "Myfuny ()" имеет 50 строк кода, в которых также вызываются другие меньшие функции. Какой из следующих кодов будет более эффективным?

void myfunction(long *a, long *b);
int i;
for(i=0;i<8;i++)
   myfunction(&a, &b);

или

myfunction(&a, &b);
myfunction(&a, &b);
myfunction(&a, &b);
myfunction(&a, &b);
myfunction(&a, &b);
myfunction(&a, &b);
myfunction(&a, &b);
myfunction(&a, &b);  

любая помощь будет оценена.

Ответы [ 11 ]

14 голосов
/ 08 октября 2010

Это преждевременная оптимизация, вам просто наплевать ...

Теперь, с точки зрения обслуживания кода, первая форма (с циклом) определенно лучше.

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

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

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

4 голосов
/ 08 октября 2010

Я бы предположил, что компилятор переведет первый вариант во второй.

3 голосов
/ 08 октября 2010

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

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

2 голосов
/ 08 октября 2010

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

2 голосов
/ 08 октября 2010

Это зависит от многих вещей, поэтому лучше всего делать это как по-разному, так и по меркам.

1 голос
/ 08 октября 2010

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

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

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

В-четвертых, если есть разница, вы собираетесь протестировать оба способа, чтобы увидеть, что лучше? Как минимум на репрезентативной выборке систем, на которых будут работать ваши пользователи?

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

1 голос
/ 08 октября 2010

Первый.Это легче читать.

0 голосов
/ 08 октября 2010

Сколько стоит myFunction(long *a, long *b)?

Если это намного больше, чем *a = *b + 1;, то стоимость вызова функции может быть настолько мала по сравнению с тем, что происходит внутри функции, что вы действительно фокусируете внимание не на том месте.

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

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

0 голосов
/ 08 октября 2010

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

В зависимости от того, что на самом деле делает myfunction, разница в шуме может быть настолько сильной, что ее невозможно обнаружить.

Этот вид микрооптимизации следует выполнять только в том случае, если выполняются все следующие условия:

  1. Вы не соответствуете жестким требованиям к производительности;
  2. Вы уже выбрали правильный алгоритм и структуру данных для рассматриваемой проблемы (например, в среднем случае плохо оптимизированная быстрая сортировка выбьет штаны из высокооптимизированной пузырьковой сортировки, а в худшем случае - быть одинаково плохим);
  3. Вы компилируете с наивысшим уровнем оптимизации, который предлагает компилятор;
0 голосов
/ 08 октября 2010

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

Итак, я написал две версии программы, итерируя по функции none (), которая не делала ничего интересного (в том числе дляпеременная).

Первый использовал правильные циклы (миллион итераций по 1000 итераций, два вложенных форса), второй сделал миллион итераций из 1000 последовательных вызовов в ничто ().

Iиспользовал команду времени для измерения.Версия с правильным циклом занимала в среднем около 3,5 секунд, а версия с последовательным вызовом - в среднем около 2,5 секунд.

Затем я попытался скомпилировать с флагами оптимизации, но gcc обнаружил, что программа практически ничего не делала, ивыполнение было мгновенным на обеих версиях = P.Не удосужились исправить это.

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

Также обратите внимание, что мои тесты ничего не делали, кроме ничего () (= P) и не являются правильнымиконтрольные показатели для рассмотрения в любой реальной программе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...