Здраствуйте, ассемблер изучаю недолго, но программирование на нем в целом понимаю очень хорошо. Помогите оптимизировать код на ассемблере по размеру (от оптимизации по скорости не откажусь также). Например, Код (Text): mov [di], al inc di можна заменить на Код (Text): stosb Хорошо, что я это знаю =) Еще я знаю замену cmp ax, 0 на or ax,ax. И mov ax, 0 на xor ax,ax. Но больше никаких оптимизаций не знаю. Поэтому прошу помочь ссылками или советами в данном вопросе. Чтобы вы поняли, с чем я имею дело, привожу кусочки кода (сам написал), которые хочется улучшить. Не откажусь и от грязных хаков =) Примечание. Работаю в FASM, 16-битный режим, плоская модель памяти (пишу нечто ОСе-подобное, если говорить прямо). Первый кусочек Код (Text): BIOS_VIDEO = 0x10 PUT_CHAR_FUNC = 0x0E ; Процедура вывода строки на екран. Строка паскалевского типа, тоесть нулевой байт - длина строки ; Адрес строки в SI Put_PASCAL_String: xor cx, cx ; CH <- 0 lodsb ; AL <- [SI]; SI++ // read first byte - string length mov cl, al ; CL <- AL mov ah, PUT_CHAR_FUNC ; AH <- 0x0E nextpascalchar: lodsb ; AL <- [SI]; SI++ int BIOS_VIDEO ; put char in AL to screen loop nextpascalchar ; loop this actions for CX symbols ret Пример использования: Код (Text): mov si, MyStr call Put_PASCAL_String MyStr db 49, 'Super duper mega Operation System ver 0.000001 (c) (q) bbb HELLO WORLD!' Этот кусок кода, на мой взгляд уже предельно оптимизирован, но вдруг кто-то сможет помочь и его улучшить?=) Второй кусочек Код (Text): READ_KEY = 0x00 KEYBOARD = 0x16 ; Ввести в AL клавишу SimpleGetKey: mov ah, READ_KEY int KEYBOARD ret ; Вывести с AL символ на екран SimplePutChar: mov ah, PUT_CHAR_FUNC int BIOS_VIDEO ret ; Считать строку символов с клавы, пока юзер не нажмет ENTER (#13). ; в DI - адрес памяти, куда считывать строку ; Считаная строка должна быть паскалевского типа, тоесть в нулевом байте - длина строки (см. выше) GetString: mov bx, di mov al, 0 stosb loop_read: call SimpleGetKey ;считали клавишу в AL cmp al, 13 jz finish_input stosb call SimplePutChar ;также нужно вывести ее на екран, чтобы юзерь видел, что вводит jmp loop_read finish_input: mov ax, di ;тут происходят вычисления длины. Можно оптимизировать? sub ax, bx dec ax mov byte[bx], al ret Пример использования приводить не буду, думаю, это понятно. Так вот, можно улучшить как-то? Третий кусочек Код (Text): mov si, [current_word] ;current_word - адрес на структуру такого формата: ; previus_adress DW ; pacalName DB ; machine code ; ret ;пример ;GETKEY: ; dw 0 ; db 6,'GETKEY' ; mov ah, 0 ; int 16h ; mov ah, PUT_CHAR_FUNC ; int BIOS_VIDEO ; ret add si, 2 ;находим адрес строки-названия именованой процедурки mov ax, si ;дальше идут манипуляции, цель которых найти адрес исполняемого кода и запустить его mov bx, 0 mov bl, byte[si] add ax, bx inc ax mov bx, ax call bx Можно упростить это? Спасибо))
В функции вывода строки вместо Код (Text): xor cx, cx lodsb mov cl, al Можно написать Код (Text): movzx cx,byte[ds:si] inc si и сэкономить 1 байт
Код (Text): GetString: mov bx, di mov byte[bx],-1 @@:call SimpleGetKey ;считали клавишу в AL stosb call SimplePutChar ;также нужно вывести ее на екран, чтобы юзерь видел, что вводит inc byte[bx] ;длина строки cmp al, 13 ;пока не нажмут Enter jnz @b ret
danbst В данном случае речь идет о вызове мощных функций и "общей" логике программы. Оптимизировать по памяти или скорости здесь _не_нужно_ (не нужно тратить много усилий на это): - Вызывая функции BIOS которые исполняют тысячи команд - оптимизация в пару тактов бессмысленна; - Вместо оптимизации данного цикла нужно думать как правильно спроектировать программу: допустим способ передачи параметров номер 1 более экономичен в 52 вызовах в разных местах кода, но есть также способ 2 который будет более экономичен в 48 случаях - что предпочесть? Первый, второй или реализовать оба для обоих ситуаций? - Читабельность - а главное - надежность программы. Иногда лучше написать на несколько байт длиннее зато надежнее.
http://asm.shadrinsk.net/uroki.htm http://democoder.ru/article/221 http://www.wasm.ru/article.php?article=vgw07 http://www.wasm.ru/article.php?article=1006014
Все это я понимаю очень хорошо! Но вместе с тем во мне живет дух "хакера" - мне приятно, если я могу сэкономить хотя бы один байт в памати. Кроме того, это уменьшает еще и исходник и в большинстве случаев увеличивает скорость, что есть не менее приятно. Все таки код, который я пишу, не будет полноценной программой, он интересен, так сказать, только в академическом плане (в смысле, научится). Насчет int, да, согласен. Поначалу было желание делать все через порты, чтобы увеличить скорость, но потом все таки решил, что размер исходника (и кода) мне на данном этапе важнее murder спасибо огромное!
Это абсолютно верное стремление и никто не советовал ничего другого. Вы полностью закончили эту программу? Если да, то оптимизируйте ее в первую очередь _по_скорости_ - потому что так вы экономите время _пользователя_. Вы _уже_ истратили достаточно усилий для минимизации этой программы, написав ее на ассемблере. Если нет - работайте над ее каркасом и расширяемостью. Если вы _спроектируете_ библиотеку (например) интерфейсных функций достаточно верно, то основная доля реализации будет _данными_ (а не кодом!) - структурами там разными и так далее. Это очень сильно повлияет на проект в целом. Еще раз - грамотное проектирование. Конкретная реализация ввода-вывода "через порты" будет на (максимум) несколько сот байт больше, но если это что-то даст в плане переносимости (или академического интереса - грамотный пример демонстрации работы с устройствами) - то это стоит сделать. Грамотный модуль по чтению-записи на USB "через порты" совсем не абстрактная задача. А если про ввод-вывод на экран - так текстовый режим - запись напрямую в видеобуфер - намного короче и быстрее BIOS. Если вам нужна графика (так ли это?) - используйте безпортовые режимы типа 13h.
Ну еще добавлю: создание функций SimpleGetKey и SimplePutChar - это шаг к "стандартизации" и упрощению читаемости. Но одновременно шаг к увеличению размера: Код (Text): B4 xx mov ah, something ;2 байта CD yy int n ;2 байта C3 ret ;1 байт ... E8 ssss call Simple ;3 байта Полезная нагрузка процедуры занимает 4 байта. Если она используется ТОЛЬКО 1 раз, то ты с выносом этих 4-х байт в процедуру увеличиваешь размер на 4 байта (ret + call). Если используется 2 раза, то ... внимание! твоя программа имеет лишних 3 байта (8 байт при непосредственном включении в код и 11 байт при процедуре + 2 вызова). Только при 6-кратном появлении вызова процедуры Simple ты наконец-то получаешь выйгрыш в 1 байт по сравнению с непосредственным включением этих команд в код. Уверен, что столько раз ты ее не вызываешь максимум 2 раза.
есть функция для ввода клавиши с "эхом" на экран. То есть вместо 2-х обращений к прерываниям будет только одно. Далее... Вообще, все прекрасно. Но... А вы уверены, что AH не изменяется после int 10h? Это не к размеру придирка, я лично накалывался. На моем компьютере все было прекрасно, а на другом - с другим BIOS и другой видеокартой - оказалось, что один из регистров портится. По логике - он может портится, я проверил - у меня сохранялся, и я обрадовался )) а есть гарантии, что на других системах это так же? Поэтому mov ah, 0eh надо внести внутрь цикла - размера не добавит, скорость существенно не изменит, но будет универсальнее.
и по 3-му кусочку - это имеет смысл только для некоторого "интерпретатора", так? То есть в программе набор именованных функций-операторов. Это уже не столько вопрос программирования, сколько организации данных. Возможно, эффективней будет список (и слово с адресом начала кода, чтоб не вычислять)? Или 2 таблицы (с именами и адресами)? Или таблицу с именами делаем не паскаль-строками, а как-то иначе? Это уже зависит от количества интерпретируемых операторов - если их 5, то возможно, что простота работы с паскаль-строками перевесит. Если их 100 - это лишние 100 байт для хранения длин строк, лучше в конец добавлять старший бит (как и делали в древних Бейсиках). Операторы на английском, их ASC-коды не могут быть больше 127 - если старший бит установлен, то это признак конца текущего оператора. В 100 байт (освобождающихся при такой организации списка имен) запросто вписывается процедура, которая будет искать имена по новым правилам.
PSR1257 )) Пользователю пофиг на время, поскольку единственный пользователь - это я =) Но если бы я делал продукт для людей, тогда да, полностью с вами согласен. Да, так ) Но сначала освою текст.FatMoon Хмм.. Подсчитал, оказывается SimpleGetKey используется 4 раза, а SimplePutChar - 9. Буду знать, спасибо. Ух, чем дальше, тем сложнее)) Спасибо за подсказку. Подскажите, что за БИОС там был - включу в список неподдерживаемых =))) (лишний байт добавить для универсальности конечно не помешает, но к универсальности я не стремлюсь. У меня только один комп, и программа пока не должна выходить за его границы) Да, так. Пишу нечто похожее на FORTH интерпретатор. Поэтому организация данных уже выходит за рамки оптимизации, там экономить на байтиках ИМХО уже нельзя. Ну... В старых FORTH-ах длина слова была ограничена 32 символами, поскольку нижние 5 бит первого байта строки - это была длина слова, а верхний бит - флаг немедленного исполнения. Поэтому, хочешь-нехочешь, байт атрибута присутствовать должен. Это к тому, что если я сейчас изменю способ реализации строк на другой - потом будет сложнее масштабировать програмку. Кроме того, ASCIIZ или подобные Бейсиковым нехочется использовать принципиально. Читал где-то про проблемы конкатенаций. Конечно, при финальной оптимизации можна будет воспользоватся и вашими советами. Но не знаю, дойду ли я до нее ))
А, понял, что я хочу оптимизировать. Код должен быть структурированым и на это жалеть байты нельзя. Но алгоритмы должны быть укорочены по максимуму. А если задуматься еще раз, то мне нужно: а) оптимальный алгоритм по количеству строчек а исходнике; б) оптимальный алгоритм по колчеству занятых байт; в) оптимальный по скорости. Тоесть, мне нужно тройная работа ) даже четверная, если учитывать изначальный исходник. Просто пока-что все алгоритмы и особенности у меня очень просты и поддаются качественному анализу. Вот и пользуюсь этим для образования. http://democoder.ru/article/221 эта статья честно поразила! Осталось найти самый быстрый вариант (или хотя бы перечень методов, как можна увеличить скорость). И еще. Я понимаю, что как ты не будешь оптимизировать бубл-сорт на ассемблере, квик-сорт на питоне будет работать вообщем говоря, на порядок быстрее. Поэтому в пункт оптимизация по скорости также входит и изменение алгоритма на более быстрый.
Для начала определитесь с определением термина "оптимальный алгоритм", это что? Написать несколько алгоритмов, выделить функцию, а потом от нее взять производную? по пункту а --> если имеется ввиду минимальное количество строчек в исходнике, то следует: а) использовать макросы или б) перейти с ассемблера на ЯВУ по пункту б --> наверное, имеется ввиду, уменьшение размера кода, а по пункту в наверное, максимальная скорость: открою вам страшную тайну --> это два взаимоисключающих понятия
Пока количество сортируемых элементов мало (~10) разницы по быстродействию между buble- и quik-sort не заметишь, но быструю сортировку можно написать и на asm, а если самому писать лень, то существует API-функция быстрой сортировки
очень спорное утверждение, если только квиксорт на питоне не реализован на подключаемом внешнем бинаре (не скомпиленный питон, а настоящий бинарь) в целом и общем желательно минимизировать разбросанные обращения к памяти. да и беготня по функциям тоже не есть хорошо. строковые операции работают медленнее соотв мувов. ммх --> ссе итд.