Экстремальная оптимизация Крис Касперски Хакер, номер #091, стр. 091-120-2 push offset file_name ; имя исполняемого файла с аргументами call system ; прямой вызов функции (почему так — см. врезку) add esp, 4 ; выталкиваем аргументы из стека (можно сделать позже) То же самое относится и к функциям файлового ввода/вывода, преобразованиям данных и т. д., и т. п. Никто же не будет спорить, что вызов fopen намного короче, чем CreateFile, а скорость исполнения у них практически та же самая, тем более что библиотека MSVCRT.DLL всегда присутствует в памяти, поскольку используются системные процессы. Windows просто спроецирует ее на наше адресное пространство — вот и все! Никакого увеличения потребляемой памяти не произойдет! Наибольший выигрыш достигается на задачах, требующих перевода двоичных данных в ASCII-представление или наоборот. Собственно говоря, программирование на ассемблере и начинается с вывода на экран числа, заданного в двоичной форме. Конечно, «вручную» разработанная и оптимизированная функция намного быстрее стандартного sprintf. Однако очень редко можно встретить программу, расходующую основное время на преобразование данных, поэтому использование библиотечных функций сокращает размер и время разработки программы. Приведенный ниже пример распечатывает число, содержащееся в регистре EAX в шестнадцатеричной, десятичной и восьмеричной форме, автоматически дописывая ведущие нули, растягивающие число до 4-х разрядов. А теперь попробуй осуществить то же самое без использования библиотек и сравни размер полученного кода! «Фрагмент программы, принимающий число в регистре EAX и выводящий его на экран в шестнадцатеричной, десятеричной и восьмеричной формах» mov eax, 666h ; число, которое необходимо вывести на экран ; // переводим число в hex, dec и oct системы исчисления в ASCII-представлении sub esp, 60h ; резервируем память под буфер, куда пойдет результат mov ebx, esp ; сохраняем указатель на буфер в регистре EBX push eax ; \ push eax ; + - передаем число для преобразования функции sprintf push eax ; / push offset s ; передаем в стек указатель на строку спецификаторов push ebx ; передаем указатель на буфер для получения результата call sprintf ; прямой вызов функции sprintf ; // вывод преобразованных данных на экран через диалоговое окно xor eax,eax ; eax := 0 push eax ; uType push eax ; lpCaption push ebx ; lpText (наши преобразованные данные) push eax ; hWnd call ds:[MessageBoxA] ; косвенный вызов API-функции MessageBox add esp, 60h + (5*4) ; выталкиваем аргументы из стека и уничтожаем буфер ... ... ... s db "%04X hex == %04d dec == %04o oct",0 ; строка спецификаторов Вызов API-функций из ассемблерный вставок При вызове API и DLL-функций из ассемблерных вставок возникает множество проблем, довольно туманно описанных в документации, прилагаемой к компилятору. Возьмем, к примеру, Microsoft Visual C++ и попробуем вызвать функцию GetVersion так, как будто мы сделали это на чистом ассемблере: __asm{ call GetVersion ; прямой вызов API-функции } Компилируем файл с настройками по умолчанию и запускаем. Программа тут же рушится. Почему? Смотрим в дизассемблере: |