Цитата: jvarg от октября 14, 2011, 11:47
Для тогдашних маленьких машин были и компиляторы высокоуровневых языков, однако именно потому для создания практических программ и использовался, в основном, ассемблер, что он позволял сократить объем программы даже на таких мелочах, как передача параметров через стек.
И не только объём программы, но и быстродействие.
Цитата: jvarg от октября 14, 2011, 11:47
Угу, только это лишние байты, что при тогдашних объемах памяти и быстродействии имело огромное значение.
Тогда лучше было не функции использовать, а goto.
Цитата: jvarg от октября 14, 2011, 11:47
Для тогдашних маленьких машин были и компиляторы высокоуровневых языков, однако именно потому для создания практических программ и использовался, в основном, ассемблер, что он позволял сократить объем программы даже на таких мелочах, как передача параметров через стек.
Ну да, передавайте через регистры... Только кто полезные значения из регистров и где сохранять будет? Правильно, в стэке.
Цитата: mnashe от октября 14, 2011, 14:06
И не только объём программы, но и быстродействие.
А нынче компиляторы умные пошли. Оптимизируют.
ЦитироватьА нынче компиляторы умные пошли. Оптимизируют.
Не факт.
Ну то что для базовых типов (a=a+b) === (a+=b) — знают.
Цитата: Python от октября 14, 2011, 15:22
Не факт.
Ой-ой-ой, таки не факт, обнаружил жуткую ересь!
c = a/b; d = a%b;даёт
mov eax, DWORD PTR [esp+20]
mov edx, DWORD PTR [esp+16]
mov ecx, edx
cdq
idiv ecx
mov DWORD PTR [esp+24], eax
mov eax, DWORD PTR [esp+20]
mov edx, DWORD PTR [esp+16]
mov ecx, edx
cdq
idiv ecx
mov DWORD PTR [esp+28], edx
Цитата: Drundia от октября 14, 2011, 21:32
Цитата: Python от октября 14, 2011, 15:22
Не факт.
Ой-ой-ой, таки не факт, обнаружил жуткую ересь!
c = a/b; d = a%b;
даёт
mov eax, DWORD PTR [esp+20]
mov edx, DWORD PTR [esp+16]
mov ecx, edx
cdq
idiv ecx
mov DWORD PTR [esp+24], eax
mov eax, DWORD PTR [esp+20]
mov edx, DWORD PTR [esp+16]
mov ecx, edx
cdq
idiv ecx
mov DWORD PTR [esp+28], edx
Вот-вот.
Задача, кстати, весьма практическая: например, при переводе числа в строку.
И это не единственный случай. Если такое делается в многократно повторяющемся цикле, разница будет ощутимой.
Хотя, конечно, на шкале между древними компиляторами высоких языков и ассемблером современные компиляторы ближе к ассемблеру.
Вот-вот, и никакие оптимизации не спасают.
Ещё есть забавная задача с арифметикой по модулю. Без ассемблера никуда.
if ((x+=rhs.x) < rhs.x) x-=n;
даёт
mov eax, DWORD PTR [edx+4]
add eax, DWORD PTR [ecx+4]
mov DWORD PTR [ecx+4], eax
cmp eax, DWORD PTR [edx+4] ;ересь
jae L2
LVL1:
sub eax, DWORD PTR [ecx]
mov DWORD PTR [ecx+4], eax
L2:
Учитывая беззнаковость операндов, условие в if'е — это просто установка CF при сложении. Стало быть ересь не нужна. К счастью, jnc===jae. Впрочем, ассемблером можно и красивее, учитывая что нам потом нужен остаток от деления.
С умножением ещё веселее. Надо получить int64 из int32*int32, а потом найти остаток от деления этого int64 на int32. Нормальный код получить не выходит, а вот добрый дядя Интел всё необходимое предусмотрел.
Впрочем, GCC всё же неплохо умеет интегрировать сишный и ассемблийный код, что избавляет от ненужной мороки.
asm (
"mul %[b0]\n\t"
"div %[d0]\n\t"
:[a] "=d" (x) // лучше "=&d"?
:[a0] "a" (x), [b0] "g" (rhs.x), [d0] "g" (n)
);
Вывод: языки высокого уровня для низкоуровневых задач не годятся.
Цитата: Drundia от октября 16, 2011, 17:36С умножением ещё веселее. Надо получить int64 из int32*int32, а потом найти остаток от деления этого int64 на int32. Нормальный код получить не выходит, а вот добрый дядя Интел всё необходимое предусмотрел.
В моей программе есть специальная функция от трёх параметров — muldiv. Она такую цепочку как раз и задействует.
Остаток от деления, правда, отбрасывается, но пока в такой цепочке он мне не был нужен.
Посчитал, сколько раз эта функция используется у меня в макрях: 46.
Цитата: mnashe от октября 16, 2011, 18:02
В моей программе есть специальная функция от трёх параметров — muldiv. Она такую цепочку как раз и задействует.
Со всеми оптимизациями код
x = ((unsigned long long)x*rhs.x) % n;даёт вполне пригодное умножение, но для остатка всё равно использует встроенную функцию для деления слишком длинных целых ___umoddi3 :( Надо будет посмотреть когда-нибудь, как она устроена.
В сложении явно использует при подобном коде больше регистров, чем надо.
Если преобразовывать уже результат умножения, то код для определения остатка вполне пригодный; но старшее слово теряется, что соответствует стандарту. Нехорошо.
Продолжаю баловаться. Похоже что if ((x+rhs.x) < rhs.x) x=x-n+rhs.x;
else x+=rhs.x;компилируется лучше (догадался, что (a+b)<a для беззнаковых — это тест кэрри-флэга, и даже догадался, что последующие операции сложения используют уже посчитанное x+rhs.x), чем
if ((x+=rhs.x) < rhs.x) x-=n; Забавно.
Для уважаемых модераторов: думаю, не помешало бы кусок темы, посвящённый компиляции сишного (сиплюсплюсного) кода в x86-ассембли вынести в отдельную тему.