Помогите оптимизировать несколько небольших кусочков кода

Тема в разделе "WASM.A&O", создана пользователем danbst, 27 фев 2010.

  1. danbst

    danbst New Member

    Публикаций:
    0
    Регистрация:
    26 фев 2010
    Сообщения:
    6
    Здраствуйте, ассемблер изучаю недолго, но программирование на нем в целом понимаю очень хорошо.

    Помогите оптимизировать код на ассемблере по размеру (от оптимизации по скорости не откажусь также). Например,
    Код (Text):
    1. mov [di], al  
    2. inc di
    можна заменить на
    Код (Text):
    1. stosb
    Хорошо, что я это знаю =) Еще я знаю замену cmp ax, 0 на or ax,ax. И mov ax, 0 на xor ax,ax. Но больше никаких оптимизаций не знаю.

    Поэтому прошу помочь ссылками или советами в данном вопросе. Чтобы вы поняли, с чем я имею дело, привожу кусочки кода (сам написал), которые хочется улучшить. Не откажусь и от грязных хаков =)

    Примечание. Работаю в FASM, 16-битный режим, плоская модель памяти (пишу нечто ОСе-подобное, если говорить прямо).

    Первый кусочек

    Код (Text):
    1. BIOS_VIDEO = 0x10
    2. PUT_CHAR_FUNC = 0x0E
    3.  
    4. ; Процедура вывода строки на екран. Строка паскалевского типа, тоесть нулевой байт - длина строки
    5. ; Адрес строки в SI
    6. Put_PASCAL_String:
    7.  xor cx, cx            ; CH <- 0
    8.  lodsb                 ; AL <- [SI]; SI++   // read first byte - string length
    9.  mov cl, al            ; CL <- AL
    10.  mov ah, PUT_CHAR_FUNC ; AH <- 0x0E
    11. nextpascalchar:
    12.  lodsb                 ; AL <- [SI]; SI++
    13.  int BIOS_VIDEO        ; put char in AL to screen
    14.  loop nextpascalchar   ; loop this actions for CX symbols
    15.  ret
    Пример использования:
    Код (Text):
    1. mov si, MyStr
    2. call Put_PASCAL_String
    3.  
    4. MyStr db 49, 'Super duper mega Operation System ver 0.000001 (c) (q) bbb HELLO WORLD!'
    Этот кусок кода, на мой взгляд уже предельно оптимизирован, но вдруг кто-то сможет помочь и его улучшить?=)

    Второй кусочек
    Код (Text):
    1. READ_KEY = 0x00
    2. KEYBOARD = 0x16
    3.  
    4. ; Ввести в AL клавишу
    5. SimpleGetKey:
    6.  mov ah, READ_KEY
    7.  int KEYBOARD
    8.  ret
    9.  
    10. ; Вывести с AL символ на екран
    11. SimplePutChar:
    12.  mov ah, PUT_CHAR_FUNC
    13.  int BIOS_VIDEO
    14.  ret
    15.  
    16.  
    17. ; Считать строку символов с клавы, пока юзер не нажмет ENTER (#13).
    18. ; в DI - адрес памяти, куда считывать строку
    19. ; Считаная строка должна быть паскалевского типа, тоесть в нулевом байте - длина строки (см. выше)
    20. GetString:
    21.  mov bx, di
    22.  mov al, 0
    23.  stosb
    24.  loop_read:
    25.         call SimpleGetKey          ;считали клавишу в AL
    26.         cmp al, 13
    27.         jz finish_input
    28.         stosb
    29.         call SimplePutChar         ;также нужно вывести ее на екран, чтобы юзерь видел, что вводит
    30.         jmp loop_read
    31.  finish_input:
    32.  mov ax, di                           ;тут происходят вычисления длины. Можно оптимизировать?
    33.  sub ax, bx
    34.  dec ax
    35.  mov byte[bx], al
    36.  ret
    Пример использования приводить не буду, думаю, это понятно. Так вот, можно улучшить как-то?

    Третий кусочек
    Код (Text):
    1.     mov si, [current_word]
    2. ;current_word - адрес на структуру такого формата:
    3. ; previus_adress DW
    4. ; pacalName DB
    5. ; machine code
    6. ; ret
    7.  
    8. ;пример
    9. ;GETKEY:
    10. ;        dw 0
    11. ;        db 6,'GETKEY'
    12. ;           mov ah, 0
    13. ;           int 16h
    14. ;           mov ah, PUT_CHAR_FUNC
    15. ;           int BIOS_VIDEO
    16. ;           ret
    17.  
    18.     add si, 2   ;находим адрес строки-названия именованой процедурки
    19.     mov ax, si ;дальше идут манипуляции, цель которых найти адрес исполняемого кода и запустить его
    20.     mov bx, 0
    21.     mov bl, byte[si]
    22.     add ax, bx
    23.     inc ax
    24.     mov bx, ax
    25.     call bx
    Можно упростить это?

    Спасибо))
     
  2. Ustus

    Ustus New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2005
    Сообщения:
    834
    Адрес:
    Харьков
    Оптимизить надо по размеру или по скорости?
     
  3. danbst

    danbst New Member

    Публикаций:
    0
    Регистрация:
    26 фев 2010
    Сообщения:
    6
    По размеру, по скорости - второстепенно.
     
  4. murder

    murder Member

    Публикаций:
    0
    Регистрация:
    3 июн 2007
    Сообщения:
    628
    В функции вывода строки вместо
    Код (Text):
    1. xor cx, cx
    2. lodsb
    3. mov cl, al
    Можно написать
    Код (Text):
    1. movzx cx,byte[ds:si]
    2. inc   si
    и сэкономить 1 байт
     
  5. murder

    murder Member

    Публикаций:
    0
    Регистрация:
    3 июн 2007
    Сообщения:
    628
    Код (Text):
    1. GetString:
    2.  mov bx, di
    3.  mov byte[bx],-1
    4.  @@:call  SimpleGetKey          ;считали клавишу в AL
    5.     stosb
    6.     call  SimplePutChar         ;также нужно вывести ее на екран, чтобы юзерь видел, что вводит
    7.     inc   byte[bx]              ;длина строки
    8.     cmp   al, 13                ;пока не нажмут Enter
    9.  jnz @b
    10. ret
     
  6. murder

    murder Member

    Публикаций:
    0
    Регистрация:
    3 июн 2007
    Сообщения:
    628
    Третий кусочек
    Код (Text):
    1. movzx ax,byte[si+2]
    2. add   ax,si
    3. add   ax,3
    4. call  ax
     
  7. PSR1257

    PSR1257 New Member

    Публикаций:
    0
    Регистрация:
    30 ноя 2008
    Сообщения:
    933
    danbst

    В данном случае речь идет о вызове мощных функций и "общей" логике программы. Оптимизировать по памяти или скорости здесь _не_нужно_ (не нужно тратить много усилий на это):

    - Вызывая функции BIOS которые исполняют тысячи команд - оптимизация в пару тактов бессмысленна;
    - Вместо оптимизации данного цикла нужно думать как правильно спроектировать программу: допустим способ передачи параметров номер 1 более экономичен в 52 вызовах в разных местах кода, но есть также способ 2 который будет более экономичен в 48 случаях - что предпочесть? Первый, второй или реализовать оба для обоих ситуаций?
    - Читабельность - а главное - надежность программы. Иногда лучше написать на несколько байт длиннее зато надежнее.
     
  8. murder

    murder Member

    Публикаций:
    0
    Регистрация:
    3 июн 2007
    Сообщения:
    628
    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
     
  9. danbst

    danbst New Member

    Публикаций:
    0
    Регистрация:
    26 фев 2010
    Сообщения:
    6
    Все это я понимаю очень хорошо! Но вместе с тем во мне живет дух "хакера" - мне приятно, если я могу сэкономить хотя бы один байт в памати. Кроме того, это уменьшает еще и исходник и в большинстве случаев увеличивает скорость, что есть не менее приятно.

    Все таки код, который я пишу, не будет полноценной программой, он интересен, так сказать, только в академическом плане (в смысле, научится).

    Насчет int, да, согласен. Поначалу было желание делать все через порты, чтобы увеличить скорость, но потом все таки решил, что размер исходника (и кода) мне на данном этапе важнее

    murder
    спасибо огромное!
     
  10. PSR1257

    PSR1257 New Member

    Публикаций:
    0
    Регистрация:
    30 ноя 2008
    Сообщения:
    933
    Это абсолютно верное стремление и никто не советовал ничего другого.

    Вы полностью закончили эту программу? Если да, то оптимизируйте ее в первую очередь _по_скорости_ - потому что так вы экономите время _пользователя_. Вы _уже_ истратили достаточно усилий для минимизации этой программы, написав ее на ассемблере.

    Если нет - работайте над ее каркасом и расширяемостью. Если вы _спроектируете_ библиотеку (например) интерфейсных функций достаточно верно, то основная доля реализации будет _данными_ (а не кодом!) - структурами там разными и так далее. Это очень сильно повлияет на проект в целом.

    Еще раз - грамотное проектирование. Конкретная реализация ввода-вывода "через порты" будет на (максимум) несколько сот байт больше, но если это что-то даст в плане переносимости (или академического интереса - грамотный пример демонстрации работы с устройствами) - то это стоит сделать. Грамотный модуль по чтению-записи на USB "через порты" совсем не абстрактная задача.

    А если про ввод-вывод на экран - так текстовый режим - запись напрямую в видеобуфер - намного короче и быстрее BIOS. Если вам нужна графика (так ли это?) - используйте безпортовые режимы типа 13h.
     
  11. FatMoon

    FatMoon New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2002
    Сообщения:
    954
    Адрес:
    Russia
    Ну еще добавлю: создание функций SimpleGetKey и SimplePutChar - это шаг к "стандартизации" и упрощению читаемости. Но одновременно шаг к увеличению размера:
    Код (Text):
    1. B4 xx   mov ah, something  ;2 байта
    2. CD yy  int n          ;2 байта
    3. C3      ret            ;1 байт
    4. ...
    5. E8 ssss call Simple     ;3 байта
    Полезная нагрузка процедуры занимает 4 байта. Если она используется ТОЛЬКО 1 раз, то ты с выносом этих 4-х байт в процедуру увеличиваешь размер на 4 байта (ret + call). Если используется 2 раза, то ... внимание! твоя программа имеет лишних 3 байта (8 байт при непосредственном включении в код и 11 байт при процедуре + 2 вызова). Только при 6-кратном появлении вызова процедуры Simple ты наконец-то получаешь выйгрыш в 1 байт по сравнению с непосредственным включением этих команд в код. Уверен, что столько раз ты ее не вызываешь :) максимум 2 раза.
     
  12. FatMoon

    FatMoon New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2002
    Сообщения:
    954
    Адрес:
    Russia
    есть функция для ввода клавиши с "эхом" на экран. То есть вместо 2-х обращений к прерываниям будет только одно.

    Далее...
    Вообще, все прекрасно. Но... А вы уверены, что AH не изменяется после int 10h? Это не к размеру придирка, я лично накалывался. На моем компьютере все было прекрасно, а на другом - с другим BIOS и другой видеокартой - оказалось, что один из регистров портится. По логике - он может портится, я проверил - у меня сохранялся, и я обрадовался :))) а есть гарантии, что на других системах это так же? Поэтому mov ah, 0eh надо внести внутрь цикла - размера не добавит, скорость существенно не изменит, но будет универсальнее.
     
  13. FatMoon

    FatMoon New Member

    Публикаций:
    0
    Регистрация:
    28 ноя 2002
    Сообщения:
    954
    Адрес:
    Russia
    и по 3-му кусочку - это имеет смысл только для некоторого "интерпретатора", так? То есть в программе набор именованных функций-операторов. Это уже не столько вопрос программирования, сколько организации данных. Возможно, эффективней будет список (и слово с адресом начала кода, чтоб не вычислять)? Или 2 таблицы (с именами и адресами)? Или таблицу с именами делаем не паскаль-строками, а как-то иначе? Это уже зависит от количества интерпретируемых операторов - если их 5, то возможно, что простота работы с паскаль-строками перевесит. Если их 100 - это лишние 100 байт для хранения длин строк, лучше в конец добавлять старший бит (как и делали в древних Бейсиках). Операторы на английском, их ASC-коды не могут быть больше 127 - если старший бит установлен, то это признак конца текущего оператора. В 100 байт (освобождающихся при такой организации списка имен) запросто вписывается процедура, которая будет искать имена по новым правилам.
     
  14. danbst

    danbst New Member

    Публикаций:
    0
    Регистрация:
    26 фев 2010
    Сообщения:
    6
    PSR1257
    )) Пользователю пофиг на время, поскольку единственный пользователь - это я =) Но если бы я делал продукт для людей, тогда да, полностью с вами согласен.
    Да, так ) Но сначала освою текст.FatMoon
    Хмм.. Подсчитал, оказывается SimpleGetKey используется 4 раза, а SimplePutChar - 9. Буду знать, спасибо.
    Ух, чем дальше, тем сложнее)) Спасибо за подсказку.
    Подскажите, что за БИОС там был - включу в список неподдерживаемых =))) (лишний байт добавить для универсальности конечно не помешает, но к универсальности я не стремлюсь. У меня только один комп, и программа пока не должна выходить за его границы)
    Да, так. Пишу нечто похожее на FORTH интерпретатор. Поэтому организация данных уже выходит за рамки оптимизации, там экономить на байтиках ИМХО уже нельзя.
    Ну... В старых FORTH-ах длина слова была ограничена 32 символами, поскольку нижние 5 бит первого байта строки - это была длина слова, а верхний бит - флаг немедленного исполнения. Поэтому, хочешь-нехочешь, байт атрибута присутствовать должен.

    Это к тому, что если я сейчас изменю способ реализации строк на другой - потом будет сложнее масштабировать програмку. Кроме того, ASCIIZ или подобные Бейсиковым нехочется использовать принципиально. Читал где-то про проблемы конкатенаций.

    Конечно, при финальной оптимизации можна будет воспользоватся и вашими советами. Но не знаю, дойду ли я до нее ))
     
  15. danbst

    danbst New Member

    Публикаций:
    0
    Регистрация:
    26 фев 2010
    Сообщения:
    6
    А, понял, что я хочу оптимизировать. Код должен быть структурированым и на это жалеть байты нельзя. Но алгоритмы должны быть укорочены по максимуму. А если задуматься еще раз, то мне нужно:
    а) оптимальный алгоритм по количеству строчек а исходнике;
    б) оптимальный алгоритм по колчеству занятых байт;
    в) оптимальный по скорости.

    Тоесть, мне нужно тройная работа ) даже четверная, если учитывать изначальный исходник. Просто пока-что все алгоритмы и особенности у меня очень просты и поддаются качественному анализу. Вот и пользуюсь этим для образования.

    http://democoder.ru/article/221 эта статья честно поразила! Осталось найти самый быстрый вариант (или хотя бы перечень методов, как можна увеличить скорость).

    И еще. Я понимаю, что как ты не будешь оптимизировать бубл-сорт на ассемблере, квик-сорт на питоне будет работать вообщем говоря, на порядок быстрее. Поэтому в пункт оптимизация по скорости также входит и изменение алгоритма на более быстрый.
     
  16. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    Для начала определитесь с определением термина "оптимальный алгоритм", это что? Написать несколько алгоритмов, выделить функцию, а потом от нее взять производную?
    по пункту а --> если имеется ввиду минимальное количество строчек в исходнике, то следует:
    а) использовать макросы или б) перейти с ассемблера на ЯВУ
    по пункту б --> наверное, имеется ввиду, уменьшение размера кода, а по пункту в наверное, максимальная скорость: открою вам страшную тайну --> это два взаимоисключающих понятия
     
  17. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    Пока количество сортируемых элементов мало (~10) разницы по быстродействию между buble- и quik-sort не заметишь, но быструю сортировку можно написать и на asm, а если самому писать лень, то существует API-функция быстрой сортировки
     
  18. qqwe

    qqwe New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2009
    Сообщения:
    2.914
    очень спорное утверждение, если только квиксорт на питоне не реализован на подключаемом внешнем бинаре (не скомпиленный питон, а настоящий бинарь)

    в целом и общем желательно минимизировать разбросанные обращения к памяти. да и беготня по функциям тоже не есть хорошо. строковые операции работают медленнее соотв мувов. ммх --> ссе итд.