Это удивило меня, потому что я всегда думал, что loop
должен иметь некоторую внутреннюю оптимизацию.
Вот эксперименты, которые я сделал сегодня. Я использовал Microsoft Visual Studio 2010. Моя операционная система - 64-битная Windows 8. Мои вопросы в конце.
Первый эксперимент:
Платформа: Win32
Режим: Отладка (чтобы отключить оптимизацию)
begin = clock();
_asm
{
mov ecx, 07fffffffh
start:
loop start
}
end = clock();
cout<<"passed time: "<<double(end - begin)/CLOCKS_PER_SEC<<endl;
Выход: passed time: 3.583
(Число меняется немного с каждым прогоном, но это морально того же размера.)
Второй эксперимент:
Платформа: Win32
Режим: Отладка
begin = clock();
_asm
{
mov ecx, 07fffffffh
start:
dec ecx
jnz start
}
end = clock();
cout<<"passed time: "<<double(end - begin)/CLOCKS_PER_SEC<<endl;
Выход: passed time: 0.903
Третий и четвертый эксперименты:
Просто измените платформу на x64. Поскольку VС++ не поддерживает 64-битную встроенную сборку, я должен поместить цикл в другой файл *.asm
. Но, наконец, результаты одинаковы.
И с этого момента я начинаю использовать свой мозг - loop
в 4 раза медленнее, чем dec ecx, jnz start
, и единственная разница между ними, AFAIK, заключается в том, что dec ecx
изменяет флаги, а loop
- нет. Чтобы подражать этому флажку, я сделал
Пятый эксперимент:
Платформа: Win32 (в следующем я всегда полагаю, что платформа не влияет на результат)
Режим: Отладка
begin = clock();
_asm
{
mov ecx, 07fffffffh
pushf
start:
popf
; do the loop here
pushf
dec ecx
jnz start
popf
}
end = clock();
cout<<"passed time: "<<double(end - begin)/CLOCKS_PER_SEC<<endl;
Выход: passed time: 22.134
Это понятно, потому что pushf
и popf
должны играть с памятью. Но, скажем, например, что регистр eax
не должен храниться в конце цикла (что может быть достигнуто путем упорядочивания регистров) и что флаг OF
не нужен в цикле (это упрощает вещи, поскольку OF
не находится в младших 8 бит flag
), тогда мы можем использовать lahf
и sahf
для хранения флагов, поэтому я сделал
Шестой эксперимент:
Платформа: Win32
Режим: Отладка
begin = clock();
_asm
{
mov ecx, 07fffffffh
lahf
start:
sahf
; do the loop here
lahf
dec ecx
jnz start
sahf
}
end = clock();
cout<<"passed time: "<<double(end - begin)/CLOCKS_PER_SEC<<endl;
Выход: passed time: 1.933
Это гораздо лучше, чем использование loop
напрямую, правильно?
И последний эксперимент, который я сделал, - это также попытаться сохранить флаг OF
.
Седьмой эксперимент:
Платформа: Win32
Режим: Отладка
begin = clock();
_asm
{
mov ecx, 07fffffffh
start:
inc al
sahf
; do the loop here
lahf
mov al, 0FFh
jo dec_ecx
mov al, 0
dec_ecx:
dec ecx
jnz start
}
end = clock();
cout<<"passed time: "<<double(end - begin)/CLOCKS_PER_SEC<<endl;
Выход: passed time: 3.612
Этот результат является наихудшим, т.е. OF
не задается в каждом цикле. И это почти то же самое, что непосредственно использовать loop
...
Итак, мои вопросы:
-
Я прав, что единственное преимущество использования цикла в том, что он заботится о флажках (на самом деле только 5 из них, на которые действует
dec
)? -
Существует ли более длинный вид
lahf
иsahf
, который также перемещаетсяOF
, так что мы можем полностью избавиться отloop
?