
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.138] |
![]() |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|||||
|
Я слышал, что правельнее(в смысле оптимальнее) такой код
чем такой
Это аргументировалось тем, что сравнение с нклём быстрая операция. Но с другой стороны вычетание медленнее, или я ошибаюсь? Или нормальному оптимизотору такие фокусы побоку? |
Сообщ.
#2
,
|
|
|
Практически побоку... можно выиграть максимум 1 такт на процессоре старше 486. Такие циклы, чтобы ето имело значение встречаются довольно редко...
|
![]() |
Сообщ.
#3
,
|
||
|
Наверное все таки
![]() А вообще какая разница с чем сравнивать, если XOR дает ответ за один такт? Ну не заполняются же регистры нулем быстрее ![]() Что касается вычитания, то тут тоже нет никакой разницы, т.к. вычитание - это тоже сложение, только в обратном/дополнительном коде. Так что скорость одна и таже. Так что разницы быть не должно.... |
Сообщ.
#4
,
|
|||
|
да не... хороший оптимизатор никаких XOR не будет делать... будет что-то вроде
Вот на етой "одной простой инструкции" и будет выигрыш в 1 такт. Если структура выражения в цикле не позволяет ее вставить, вообще не будет выигрыша. |
Сообщ.
#5
,
|
|
|
Такой трюк хорош в таких случаях:
vector <double> a; зуб даю, что вот это for(int i = a.size(); --i>=0; ) { } будет работать быстрее, чем вот это: for(int i = 0; i<a.size(); i++ ) { } |
Сообщ.
#6
,
|
|
|
for(int i = 0;i!=max;i++)
00411A05 mov dword ptr [i],0 00411A0C jmp main+37h (411A17h) 00411A0E mov eax,dword ptr [i] 00411A11 add eax,1 00411A14 mov dword ptr [i],eax 00411A17 mov eax,dword ptr [i] 00411A1A cmp eax,dword ptr [max] 00411A1D je main+41h (411A21h) for(int i = max;i!=0;i--) 00411A05 mov eax,dword ptr [max] 00411A08 mov dword ptr [i],eax 00411A0B jmp main+36h (411A16h) 00411A0D mov eax,dword ptr [i] 00411A10 sub eax,1 00411A13 mov dword ptr [i],eax 00411A16 cmp dword ptr [i],0 00411A1A je main+3Eh (411A1Eh) это не одно и тоже ? или я, что то не правильно понимаю? |
Сообщ.
#7
,
|
|||||
|
Ну я тоже чего-то не понимаю (асм тоже не очень к несчстью) Но имелось ввиду(но не вкоем случае не утверждю), что эта инструкция выполниться быстрее
ввиду содержаня в ней "мифического" нуля |
Сообщ.
#8
,
|
|
|
По тактам - одно и то же.
Но это ведь компиллировалось без оптимизации по скорости? Если оптимизировать по скорости (ключ O2), то 1)for(int i = max;i!=0;i--) сведется к loop: ... dec esi jne loop 2)for(int i = 0;i!=max;i++) если max не будет изменяться, и не будет использоваться i, то тут будет код как в 1, иначе inc esi cmp esi, ebx ;max jne loop |
Сообщ.
#9
,
|
|
|
Ну тут можно вспомнить еще что ++i эффективнее чем i++.
Но вообще Страуструп писал что хороший оптимизирующий компилятор должен выдавать одинаковый код. Так афэйк, многие современные компиляторы заменяют инкрементный цикл на декрементный - как раз чтобы выиграть на сравнении с нулем. А вообще ни разу в жизни не сталкивался с ситуацией где такие изыски позволяли достичь реального увеличения производительности. Ну не писал я Real-time системы ![]() А времен всяких там 8086 с 64кб не застал.... |
Сообщ.
#10
,
|
|||
|
Выходит , что первое быстрее так как там на одну комманду меньше? |
Сообщ.
#11
,
|
|
|
Да, такой for(int j=SIZE;j!=0;j--) работает на 1 микрооперацию быстрей.
О смысле оптимизации - особенно в цикле она важна, если этот SIZE = много-много-много. |
Сообщ.
#12
,
|
|
|
Уточню
![]() ![]() |
![]() |
![]() |
||
|
На самом деле прикол в том, что при инициализации выполняется: * в первом случае: mov dword ptr [i],0 * во втором случае: mov eax,dword ptr [max] mov dword ptr [i],eax . Завто внутри цикла (на каждой итерации) будет: * в первом случае: - в начале цикла: mov eax,dword ptr [i] add eax,1 mov dword ptr [i],eax ; (что уже само по себе извращение, когда можно сделать inc dword ptr [i]) - в конце цикла: mov eax,dword ptr [i] ; соптимизировать эти две инструкции нельзя, cmp eax,dword ptr [max] ; если цикл организован именно таким образом je main+41h * во втором случае: - в начале цикла: mov eax,dword ptr [i] sub eax,1 mov dword ptr [i],eax - в конце: cmp dword ptr [i],0 00411A1A je main+3Eh . Т.е, в первом случае приходится на 1 инструкцию больше (mov), (если отключить кэш, то разница может быть ощутима, т.к. эта инструкция обращается к памяти). Кстати, даже если включить оптимизацию, то скорее всего значение переменной будет записано в регистр. На скорость это не повлияет, но тогда будет занят на 1 регистр больше (соответственно, его нельзя будет юзать при оптимизации чего-то другого, например, второго вложенного цикла). . --------------------------------------------- . Теперь по поводу того, что глаголил dimedrol... Сравнение/присвоение нулю ничуть не быстрее сравнения/присвоения другому числу. Более того, на современных процах операции с цифрами занимают столко же времени, сколько и операции с регистрами (причём, не важно, eax это или нет). Аналогично и с inc и dec (т.е. j++ и j-- работают одинаково). . Здесь фокус в другом. Если включена оптимизация, то (как уже сказал rcz) в первом случае будет генериться dec esi cmp esi,ebx jne loop а во втором dec esi jne loop т.е. на 1 инструкцию меньше (cmp esi,0 здесь не нужен, т.к. jne будет прыгать, если esi=0, а вот если esi=ЧтоТоДругое, то jne будет прыгать только если выполнен cmp). Надеюсь, все всё поняли ![]() . Важно понимать такую вещь. Делая цикл: for(int j=SIZE;j!=0;j--) вы присваиваете переменной j значение SIZE один раз, а далее в цикле делаете inc (или dec) и сравнение j с константой. . Делая же цикл: for(int j;j!=SIZE;j++) вы присваиваете переменной j один раз значение 0, а в цикле делаете inc (или dec) и сравнение j с SIZE. Соответственно, если SIZE - это функция, то её нужно будет вызывать на каждой итерации, следовательно, скорость снижается. . ------------------------------------------ . Поэтому (мораль всей басни такова) второй способ будет аналогичен первому только в двух случаях: * если SIZE-это константа и оптимизация выключена; * если SIZE-это константа, но мы делаем цикл не от/до нуля, а от/до другого числа. Иначе лучше юзать первый вариант ![]() |
Сообщ.
#14
,
|
|||||||
|
Там все гораздо сложнее... Выполнение элементарных операций (аддитивные, сравнения, пересылки ...) может совмещаться (две инструкции в за один такт, или декодирование одной с выполнением другой), есть буфер предсказания переходов, конвейер. И просто так взять и посчитать количество тактов для последовательности инструкции уже достаточно сложно, несмотря на то, что ета "внутрипроцессорная" оптимизация описывается небольшим числом довольно простых правил. ![]() Вот напрмер, время выполнения двух почти одинаковых последовательностей команд может отличаться в 1.5-2.5 (от полутора до двух с половиной) раз, в зависимости от состояния конвейера кода и данных и расположения данных в памяти:
и
... --- если кто сильно верит в свои способности, попробуйте оптимизировать ассемблерный код, который выдает MSVC 6 при включенном /Ox (а в нем, как минимум, есть одно лишнее умножениеи вычитание), вот для такой процедуры вычисления первых 1 000 000 простых:
![]() |
![]() |
Сообщ.
#15
,
|
|
Спаривание команд (когда две инструкции выполняются одновременно) было на Pentium-I и Pentium-MMX
![]() В любом случае схема всех for'ов одна и та же. Только добавляются или исчезают команды, так что... |