Эффективность кодогенерации си компиляторов
Желая продемонстрировать превосходство ассемблера над си, обычно берут программы типа "hello, world!" и сравнивают размеры _откомпилированных_ файлов, причем, ассемблер использует прямые вызовы API-функций GetStdHandle()/WriteFile(), а программу на си заставляют обращаться к printf(), которая тащит за собой библиотеку времени исполнения (она же Run Time Library или, сокращенно, RTL). Ну и где же здесь честность?! Как будто на си нельзя программировать без RTL! Можно— для этого достаточно переименовать функцию main() во что-то другое, указав линкеру точку входа в файл вручную. Размер откомпилированного файла сразу же сократится, вплотную приближаясь (или даже совпадая) с ассемблированным файлом, но 99% пространства будет занимать служебная информация PE-формата и место, оставленное линкером для выравнивания секций. На этом фоне различия между компилятором и ассемблером становятся совершенно незаметными!
Мы же поступим иначе — напишем небольшую программку, например, вычисляющую CRC8 (большая — просто бы не уместилась в статью), а затем откомпилируем ее и, прогнав полученный файл через дизассемблер, посмотрим — насколько эффективно компилятор справился со своей задачей и какой простор он оставил нам для ручной оптимизации.
Исходный текст подопытной функции выглядит так:
CRC(unsigned char *p, int n)
{
int a; unsigned char crc=0;
for (a=0;a<n;a++) crc += p[a];
return 0 - crc;
}