http://ache.vniz.net/demos.html охуительная история:
Своеобразный программисткий подвиг совершил Дима Бурков. В то время начали появляться первые PC. Unix на них выглядел неубедительно. Linux еще не появился, зато повился Venix. Хачить его было невозможно - не было исходных текстов ядра. Дима Бурков реассемблировал ядро, потом писал программы на Си, которые давали тот же текст ассемблера - так появились тексты ядра на Си ... работа не для слабонервных.
Проверял GCC на предмет того, как он умеет рекурсию оптимизировать.
Вот такая штука
unsigned int plus(unsigned int a, unsigned int b)
{
if (b == 0) return a;
return plus (a+1, b-1);
}
Относительно успешно сворачивается сложение. Получается такая шняга:
movl %edi, %eax
addl %esi, %eax
ret
Хотя в идеале можно было бы обойтись
leal (%rsi,%rdi), %eax
ret
Что касается умножения, там ситуация более печальная
inline unsigned long int product_0(const unsigned int a, const unsigned int b, const unsigned long int tmp)
{
if (b == 0) return tmp;
return product_0(a, b-1, tmp+a);
}
unsigned long int product(const unsigned int a, const unsigned int b)
{
return product_0(a, b, 0);
}
В ассемблере получается такая фигня
product:
.LFB34:
.cfi_startproc
xorl %eax, %eax
testl %esi, %esi
je .L7
leal -1(%rsi), %eax
mov %edi, %edi
addq $1, %rax
imulq %rdi, %rax
.L7:
rep
ret
.cfi_endproc
Тут оно зачем-то зануляет значение регистра, в котором хранится возврашемое из функции значение и сравнивает с нулем значение регистра, в котором в функцию передается число. Если ноль то прыгаем в конец функции, возвращая 0. Тогда внезапно появляется смысл в этом rep ret http://repzret.org/p/repzret/
Why? Because “The processor is unable to apply a branch prediction to the single-byte near-return form (opcode C3h) of the ret instruction.” Thus, “Use of a two-byte near-return can improve performance”, because it is not affected by this shortcoming.
Ну а дальше через leal из регистра rsi число копируется в eax, уменьшаясь при этом на 1 (нахрена?) и потом из регистра edi двигается в edi (НАХРЕНА??), увеличиваем rax на 1 через addq (ну тут понятно зачем, перед этим ведь оно было непонятно зачем уменьшено на 1, но нахрена уменьшать и потом увеличивать? И вообще, для увеличения на 1 лучше incq использовать) ну и в итоге компилятор таки вставляет инструкцию imulq. Распознать умножение в этой рекурсивной хрени компилятор смог, но при этом как-то через жопу все, нагенерировал кучу говна всякого. Можно было намного проще сделать
movl %esi, %eax
imull %edi, %eax
gcc version 4.5.1 если что
http://govnokod.ru/13470#comment189390
А я ассемблерщика знаю. Пишет биосы. Играет с ботами в квейк. С работы уходит последним. В 40 живет с мамой.
Прямо таки герои нашего времени...
https://i.imgur.com/8DQTtCs.png
Процесс обучения сишкоасмоебству. Указатели на функции. Watcom Debugger. XP. Виртуалка.
http://ideone.com/W1XMHh - код
https://research.microsoft.com/en-us/um/people/simonpj/papers/ndp/haskell-beats-C.pdf
Abstract
Stream fusion [6] is a powerful technique for automatically transforming high-level sequence-processing functions into efficient implementations. It has been used to great effect in Haskell libraries for manipulating byte arrays, Unicode text, and unboxed vectors. However, some operations, like vector append, still do not perform well within the standard stream fusion framework. Others, like SIMD computation using the SSE and AVX instructions available on modern x86 chips, do not seem to fit in the framework at all.
In this paper we introduce generalized stream fusion, which solves these issues. The key insight is to bundle together multiple stream representations, each tuned for a particular class of stream consumer. We also describe a stream representation suited for ef ficient computation with SSE instructions. Our ideas are implemented in modified versions of the GHC compiler and vector library. Benchmarks show that high-level Haskell code written using our compiler and libraries can produce code that is faster than both compiler- and hand-vectorized C.
На ассемблере такие вещи надо делать. Алсо, тут http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?test=all&lang=gcc&lang2=ghc&data=u64q хаскель сливает сишке почти по всем пунктам. И я бы не сказал, что кода на х-е сильно меньше, чем кода на Си
http://gcc.1065356.n5.nabble.com/Ways-to-fill-the-stack-td912561.html#none
Если через запятую объявлять члены массива из char в C и откомпилить это в GCC, оно это запишет как куча mov-ов по байтику
sztfg - я
Тут я отвечу на вопрос "Зачем нормальный программист должен знать ассемблер?" но сначала пятиминутка ненависти
Ну собственно, есть такая тема, что люди, которые не то что ассемблера, даже сишку толком не пробовавшие, пишут на своих жабоскриптах такую дикую хуиту с точки зрения оптимизации, что просто пиздец. Ибо нет нифига понимания(даже примерного) в какие такие машинные инструкции будет оттранслирована та хуерга, которую наплодили эти "программисты".
Раньше мой дед на целероне 400 мгц 256 метрами ОЗУ успешно смотрел интернет на несвежем дистрибутиви GNU/Linux. Но потом тупые индусы понаделали тормозных жабаскриптов и дед начал жаловаться на появление какого-то непонятного окошка "скрипт недоступен". Он еще начал интересоваться, что такое скрипт и на что он должен отвечать. Ну типа типичные последствия. Вообще, дофига сайтов адово тормозят от этих говен, поэтому у меня NoScript всегда наготове, ибо от этого говна тормозятся даже современные браузеры на современном железе. Это ж надо было дойти до того, чтобы делать абгрейд из-за того, что какие-то *** понаписали быдлокода на JS? Если вы у себя на сервер-сайде ворочаете всякие тормозные питоны, похопе и Node.js то это ваши личные половые проблемы. Но вот когда вы кормите этим JS-говном МОЙ процессор, тут уж я на это вынужден применять меры, вроде NoScript
http://www.opennet.ru/opennews/art.shtml?num=36962 вот на такое клиенд-сайд веб программирование (если его сильно запаковать в сандбокс) я согласен. Есть еще такая тема, как asm.js http://habrahabr.ru/post/171561/ но это всё полумеры.
А теперь по теме, нафига вообще ASM. Любой (за исключением CUDA и прочей GPU-ускоренной хреноты) код на любом ЯП в итоге транслитуется в опкоды и переваривается центральным процессором (всякие CUDA и опенжл передаются в видеокарту и перевариваются через GPU). Я лично не против, чтобы кто-нибудь писал на языках, подобных Java, JavaScript, Python, Ruby и что там еще... Вся беда в том, что для эффективного программирования даже на таких ВЫСОКОУРОВНЕВЫХ ЯП как эти, необходимо хотя бы примерно представлять, в какой машинный код преобразуются ваши высокоуровневые конструкции.
Иногда приходится смотреть на сгенерированный сишным компилятором код, чтобы понять причины тормозов. Чтение ассемблерного выхлопа GCC позволило мне выявить хреновую оптимизацию записывания байтиков в стек, вот http://gcc.1065356.n5.nabble.com/Ways-to-fill-the-stack-td912561.html почитайте (да, я знаю, у меня плохой английский)
Понимание архитектуры ЭВМ, недостатков некоторых вещей (типа NULL-terminated string http://www.joelonsoftware.com/articles/fog0000000319.html ) позволяет избегать ошибок на более высоких уровнях.
Меня тут обвиняли что я статейки какие-то даю, так вот тут я их действительно выдам
http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html
https://lwn.net/Articles/250967/
http://www.insidepro.com/kk/145/145r.shtml
https://savannah.nongnu.org/projects/pgubook/
И вот типа цитата, но я не вполне согласен с ней. Иногда ASM нужен именно для написания
Целью обучения программированию на асме, как ни странно, не может быть само по себе программирование на асме, это навык (если его рассматривать с профессионально-коммерческой точки зрения) абсолютно бессмысленный. Никто никогда не пишет на асме. Но при этом опыт такого писания жизненно необходим, чтобы примерно представлять себе, что на самом деле происходит при выполнении программы. И с этой колокольни, вообще говоря, пофигу, какой ассемблер и под какой процессор изучать. Ну, допустим, процессор всё-таки желательно использовать живой и реально существующий, чтобы не оставалось ощущения, что «с настоящим точно не получится», но и только.
Критика приветствуется