                         BIEW изнутри
                =============================

     Это  руководство  документирует  внутреннюю архитектуру  BIEW   и
замечания по разработке. Основные цели этого документа - сообщить Вам,
как расширить  возможности  проекта  и  помочь  Вам  понять  его.  Это
руководство написано исходя из предположения, что Вы знакомы с  языком
программирования "C" и основными принципами программирования.


Содержание
===========
0. Введение
1. Иерархическая структура
1.1. Как это работает
1.2. Основы плагинов и аддонов
1.3. Почему biewlib
2. Как расширить или портировать проект
2.1. Создание плагинов и аддонов
2.2. Портирование проекта
3. Примечания по исходным текстам
3.1. Размещение исходников
3.2. Структура исходников
3.3. GNU makefile
4. Примечания по оптимизации
4.1 Несколько слов о соглашениях вызовов функций
4.2. Примечания по оптимизации исходных текстов
4.3. Тесты производительности
5. Окончание

0. Введение
===========
   Biew это модульный проект, базирующийся на  технологии  плагинов  и
аддонов. Любой новый плагин  и  аддон  может  быть  легко  добавлен  в
проект, равно как и удалён из проекта. Как средство  взаимодействия  с
ОС  и  компьютером,  он  использует  библиотеку,  называемую  biewlib.
Существуют две причины рождения и существования biewlib:
 - Проект родился в 16-ти разрядной DOS  среде  с  бедными  средствами
   разработки
 - Переносимость на не-POSIX системы


1. Иерархическая структура
==========================
                                    Плагины авто уровня:
                                    +--------------------+
                               +----| Все файлы в        |
           +-----------+       |    | plugins/bin        |
           | Различные |       |    +--------------------+
           | аддоны    |       |             ^
           +-----------+       |             |             Плагины II ур-ня:
                 |             |             v              +---------------+
                 |             |     Плагины I ур-ня:       |plugins/nls    |
   biewlib:      | Базов.уров.:|     +-------------+      +-|??? в будущем  |
 +-------------+ +-*============*    |  binmode.c  |      | +---------------+
 | OS and CPU  |===# biew.c     #----|  hexmode.c  |      | +----------------+
 | depended    |===# mainloop.c #    |  textmode.c |------+ | Различные      |
 | library     |   *============*    |  disasm.c   |--------| дизассемблеры  |
 +-------------+         |           +-------------+        | plugins/disasm |
                         |                                  +----------------+
                         |
             +----------------------+
             |   утилиты biew:      |
             | biewutil, bin_util   |
             | bconsole, biewhelp,  |
             | events, ...          |
             +----------------------+

1.1. Как это работает
---------------------

   Если Вы хотите узнать детали взаимодействия исходников - Вы  должны
установить  DOXYGEN  на  который  ориентированы  комментарии  исходных
текстов проекта. Коротко о взаимодействии:

 -  При  старте  программы  управление  попадает   в   функцию   main,
  определённую  в  biew.c.  Здесь   программа   инициализирует   себя,
  анализирует командную строку, читает файл .ini, создаёт общие  окна,
  инициализирует плагины  и  адоны   и   передаёт  управление  в  файл
  mainloop.c.
 - В модуле mainloop.c определён  основной  цикл  обработки  сообщений
  программы.   Он   работает   аналогично   циклу    "GetMessage     -
  DispatchMessage" из Win3.1 SDK с реализованной callback функцией.
- После получения  сообщения  о  выходе  он  возвращает  управление  в
  функцию  main.  После  получения  управления  снова   функция   main
  деинициализирует  программу,  сохраняет  переменные  в  .ini  файле,
  отключает плагины  и аддоны,  уничтожает все  глобальные  объекты  и
  завершает выполнение.
Все другие модули является утилитарными для этого цикла.

1.2. Основы плагинов и аддонов
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   Сегодня в проекте существуют три класса плагинов:
- авто уровня
- I уровня
- II уровня

   Плагины  автоуровня,  в  основном,   разработаны    для   поддержки
файловых форматов. Плагины этого класса - автоматические плагины.  Это
означает:
- Вы не можете видеть и обнаружить их в меню, подсказках и т.д.
- Вы не можете активизировать такие плагины по Вашему усмотрению.
Во время инициализации  BIEW  проводит  опрос  плагинов  автоуровня  о
просматриваемом формате  файла  и  назначает  первый  плагин,  который
знаком с этим форматом, как обслуживающий  открытый файл. Этот  плагин
переходит в активное состояние. Все  остальные  плагины  этого  класса
переходят в отключенное состояние. Единственный способ  увидеть  какой
плагин автоуровня активен это вызвать  утилиту  "File  information"  и
прочитать информацию об обнаруженном формате файла.

  Плагины I уровня - это рабочие  лошадки  проекта.  Они  представляют
основные режимы просмотра файла. Плагины этого класса как и II  уровня
не автоматические плагины. Это означает:
- Вы можете увидеть и найти их в меню, подсказках и т.д.
- Вы можете активизировать такие плагины по своему желанию.
Во время инициализации BIEW проводит опрос плагинов I  уровня  о  том,
кто первый сможет оперировать с просматриваемым файлом. Основная  идея
такого опроса это реализация попытки разделить  текстовые  и  бинарные
файлы.  В  любом  случае,  планины  I  уровня  могут  быть  выбраны  и
активизированы  через  меню.  Когда  плагин  I  уровня   активизирован
программой  или  пользователем  он  проводит  опрос  и   инициализацию
плагинов II уровня, которые соединяются только с плагином I уровня.

В принципе,  в  будущем  могут  быть  добавлены  плагины  III  уровня,
которые будут контактировать только с плагинами II уровня  и  т.д.  до
бесконечности.

Пример, для понимания различий между плагинами автоуровня, I уровня  и
II уровня:
1. Мы открыли файл Windows PE формата.
2. Как обслуживающий бинарные структуры  этого  файла  будет  назначен
плагин автоуровня  для  PE  формата.  (В  принципе,  мы  должны  иметь
возможность для просмотра  этого  файла  как  NE  или  любого  другого
формата, но если пользователь знает об этом он должен  отредактировать
сигнатуру файла и рестартовать программу или перечитать  файл.  Т.е. в
основном, такие особенности бессмысленны).
3. Как смотреть  этот  файл.  Программа  обнаруживает  этот  файл  как
бинарный и автоматически  выбирает  viewer  бинарного  режима.  Но  мы
должны иметь  возможность  изменить  режим  просмотра  и  пользователь
может сделать это через меню.
4. Пользователь выбирает режим дизассемблера. В  основном,  PE  формат
разработан для Intel-32 архитектуры и плагин дизассемблера  I  уровня,
делая запрос к активному плагину  автоуровня  (PE  формат)  узнает  об
этом и назначает ix86 дизассемблер как дизассемблер по  умолчанию  для
этого файла. Но мы должны иметь возможность изменить  дизассемблер  на
любой другой, и пользователь  может  сделать  это  через  меню.  Но  в
данном случае всю работу для этого производит плагин I уровня  (плагин
дизассемблера).
5. Пользователь хочет разрешить (to resolve) все внутренние  ссылки  в
просматриваемом файле и получить полный  дизассемблерный  дамп  файла.
Эту работу производит плагин  I уровня.  Он  контактирует  с  плагином
автоуровня, который знает этот формат. Плагин I уровня  в этом  случае
это средство  взаимодействия  между  плагином  II  уровня  и  плагином
автоуровня. Кстати, плагин II уровня также знает о разрешении  ссылок,
но он строит все запросы для плагина I уровня.
6. Пользователь хочет сохранить результат  в  файл.  Файловая  утилита
также строит все запросы к плагину I уровня и этот  плагин  производит
всю работу для  связи  между  плагинами  других  уровней,  но  выводит
результат в файловый поток вместо экрана.
Итого: Роль плагинов  I  уровня  реализация  общего  интерфейса  между
программой  и   плагинами  остальных  уровней  и  содержать  некоторые
общие функции для плагинов  следующего  уровня,  которые  абстрагируют
такие плагины от  некоторых  особенностей  специфичных  для  носителей
информации.
В терминах C++ плагин I уровня обеспечивает интерфейс базового  класса
для плагинов следующего уровня.

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

1.3. Почему biewlib
~~~~~~~~~~~~~~~~~~~~~
   Как  это  описано  выше   существуют   две   причины   рождения   и
существования biewlib:
 - Проект родился в 16-ти разрядной DOS  среде  с  бедными  средствами
   разработки
 - Переносимость на не-POSIX системы
   Biewlib не  претендует  на  место  заменителя  C  и  многих  других
существующих библиотек. К сожалению, многие темы  в  biewlib  родились
как средство борьбы с ошибками стандартных библиотек и не соответствия
свойств их функций документации. Некоторые части biewlib были  рождены
как средство реабилитации мёртвых и заброшенных  библиотек. Для  этого
существуют исторические причины.  Когда  проект  родился  существовало
много неизвестных сегодня фирм с популярными продуктами. Часть из  них
сейчас в зоне Windows, часть  приобретены  другими.  Если  рассмотреть
biewlib по частям то получится следующее:
- bbio -     средство   кеширования   бинарных   потоков    (Семейство
             функций, работающих с FILE * потоками во многих  IDE  (по
             крайней  мере  в  ранних  версиях)  содержат  ошибки  при
             работе с бинарными файлами).
- biewlib  - содержит функции, которые по разному реализованы в  posix
             и   ms-совместимых   библиотеках   и   небольшой   набор,
             стремящийся к увеличению, полезных утилит,  отсутствующих
             во всех C-библиотеках - file_ini - уникальная  библиотека
             работы с ini файлами.
- pmalloc -  реализует некоторые полезные механизмы работы  с  памятью
             в режиме вытеснения. (Может быть я не  прав  -  но  я  не
             нашёл никаких средств  в  стандартном  наборе  библиотек,
             сигнализирующих программе о нехватке памяти в системе).
- twin     - оконная  библиотека  совместимая  по  логике   работы   с
             оконной библиотекой TopSpeed C JPI (window.h) на  которую
             опирался проект с самого начала.  (Мне  показалось  легче
             создать полный аналог этой  библиотеки  чем  переписывать
             весь проект под другие окна. К тому же  имея  собственную
             библиотеку окон - я  её  значительно  расширил  и  сделал
             переносимой).
sysdep/    - всё, что  находится  в   этом   подкаталоге   нужно   для
             реализации  низкоуровневого  интерфейса  к   операционной
             системе. Надеюсь - там всё и  так  понятно  что  к  чему.
             Может  быть  только  один  вопрос  -   почему   fileio.c?
             Удивительно, но даже порт  gcc  под  os/2  -  emx-0.9c(d)
             содержит ошибку при работе с файлами  через  open,  read,
             write,   close.   Следовательно   fileio   (как   и   всё
             остальное) - гарантия  стабильной  работы   проекта   без
             привязки к IDE.
Безусловно, сам biew использует вызовы стандартной  C  библиотеки,  но
это в основном ANSI  совместимые  функции  (ISO  -  слишком  узок  для
такого проекта, как biew,  POSIX  -  во  многих  версиях  C-библиотеки
реализован или криво или с  ошибками).  В  любом  случае  используемое
подмножество функций в biew не  вызывает  проблем  при  перекомпиляции
его  под  многими  портами  gcc,  MSC,  Watcom,   TopSpeed.   Хотя   -
безусловно, если работа какой-либо  функции  вызывает  подозрения,  то
правильнее взять её реализацию из гарантированно  работающего  проекта
с открытыми исходниками, чем  отвергать  систему  разработки  (Пример:
qsort - lfind, которые пришлось взять из проекта djgpp.) или искать  и
исправлять ошибку в ручную.


2. Как расширить или портировать проект
=======================================

    В основном проект расширяется по трём направлениям:
- Добавление и регистрация нового плагина.
- Добавление и регистрация нового аддона.
- Перенос  проекта   под  новую   вычислительную   архитектуру   и/или
  операционку.
    Все остальные случаи - это переписывание  идеологии  проекта и/или
латание ошибок и неточностей.

2.1. Создание плагинов и аддонов
--------------------------------

    Для создания нового плагина или аддона необходимо создать  объект,
интерфейс  которого  должен  реализовываться   через   соответствующую
структуру.  Хотя  такой  вариант  не  самый  идеальный  для   будущего
расширения возможностей таких объектов, но  пока  выбрана  именно  эта
схема взаимодействия частей программы, к  тому  же  она  не  исключает
возможность  реализации  объектов,  как   внешних   модулей   в   виде
динамически связываемых библиотек и пр.
    Для создания плагинов любого уровня, всегда можно  воспользоваться
пустой заготовкой, которые представлены на каждом уровне:
- автоуровень: plugins/bin/bin.c
- I уровень:   plugins/binmode.c
- II уровень:
  - дизассемблер:              plugins/disasm/null_da.c
  - поддержка кодовых страниц: plugins/nls/russian.c
Практически все (кроме russian.c) файлы содержат  минимальный  уровень
функциональности допустимый при создании объектов такого класса.
  В файле reg_form.h содержится объявление интерфейсов,  и  их  полное
описание, к плагинам (исключая II и выше уровни) и аддонам.
  Описание интерфейса плагинов II уровня находится  в  соответствующих
заголовочных файлов для плагинов уровня I.
  По  принятым  правилам: все  файлы,  реализующие  интерфейсы  должны
располагаться в соответствующих тематических каталогах и содержать  не
более одного интерфейса на файл, что в будущем может быть  выделено  в
отдельный модуль.
  После написания кода, последнее, что нужно  сделать - это  поправить
соответствующие makefile'ы, с  помощью  которых  планируется  собирать
проект.

2.2. Портирование проекта
-------------------------

   Задача портирования проекта одна из наиболее лёгких.  Вся  системно
зависимая  часть  проекта   находится   в   каталоге   biewlib/sysdep.
Структура подкаталогов  строится  как  CPU/OS.  Исключение  составляет
подкаталог generic, где находится код, который может использоватся  на
любой  платформе.   Внутри   этого   подкаталога   также   расположены
подкаталоги posix и unix. Каталог posix -  содержит  реализацию  части
функций, которые являются общими для всех полностью  posix-совместимых
операционных систем. POSIX не может полностью реализовать всё  сам  по
себе, таким образом компиляция просто использующая TARGET_OS=posix  не
может быть завершена. Каталог unix - содержит реализацию всех  функций
для  полностью  unix-совместимых  операционных   систем.   Большинство
систем Unix, коренным образом, очень похожи. К сожалению,  сегодня  не
существует  единого  стандарта,  подобного   POSIX,   для   реализации
консольных (впрочем, как и графических) приложений,  и  даже  если  он
появится, то остается куча существующих операционных  систем,  которые
не будут с ним совместимы или, по крайней мере,  систем разработки, не
имеющих подходящей реализации C-библиотеки для этого. В связи с  этим,
приходится  реализовывать  придуманный  для  программы  стандарт   для
каждой  операционной  системы   в   отдельности.   (Если   операционка
полностью unix-совместимая, то вряд ли нужно портировать проект,  хотя
вряд ли кого устроит время ожидания реакции программы в этой модели).
   Все   системно-зависимые   функции   хорошо   документированы     в
соответствующих заголовочных файлах, расположенных  в  biewlib  и  его
подкаталогах.
   Если во время портирования  проекта  какой-либо  из  файлов   может
быть  взят  из  уже  написанной  реализации,  он  может   быть   легко
использован посредством включения  этого  файла  во  вновь  созданный.
Пример:
   Файл  /biewlib/sysdep/ia32/linux/fileio.c   содержит   единственную
строку:
#include   "biewlib/sysdep/generic/linux/fileio.c"
  Таким образом реализуются  переносимые  на  любые  файловые  системы
unix-ссылки. После написания всего кода, последнее что нужно,  сделать
это поправить соответствующие makefile'ы с помощью которых планируется
собирать проект.

3. Примечания по исходным текстам
=================================

   Исторически  сложилось так,  что  проект  родился  и  долгое  время
существовал в DOS'е.  В  связи  с  этим  сложились  следующие  правила
разработки исходных текстов:
- Весь исходный код программы написан в 866 кодовой странице.
- Все имена файлов удовлетворяют схеме 8.3, без  символьных  ссылок  и
  т.п. (Проект должен собираться в DOS окружении).

3.1. Размещение исходников
--------------------------

  В каком каталоге разместить исходные тексты не имеет  значения.  Все
пути, которые использует проект - относительные.

3.2. Структура исходников
-------------------------

  Иерархия дерева исходников очень проста. Верхний уровень  начинается 
с каталога, где расположены  исходные  тексты   программы.   Следующая
картинка объясняет структуру дерева исходников:

/          - верхний уровень содержит программу точки входа и общие утилиты
addons     - содержит различные аддоны
 sys       - содержит системно-зависимые аддоны
 tools     - содержит общие аддоны
biewlib    - содержит библиотеку называемую biewlib
 sysdep    - содержит все системно и ЦПУ зависимые файлы и имеет структуру CPU/OS
bin_rc     - содержит различные готовые к использованию двоичные и текстовые файлы
doc        - содержит документацию
hlp        - содержит help проекта и некоторые исходники help'а
mk_files   - содержит различные makefile'ы для не GNU make утилит
plugins    - содержит различные плагины
 bin       - содержит плагины автоуровня
 disasm    - содержит различные дизассемблеры
 nls       - содержит поддержку национальных языков
testlab    - содержит различные тестовые программы и файлы
tools      - содержит вспомогательные утилиты

3.3. GNU Makefile
-----------------

  Процесс   построения   проекта   управляется   makefil'ом,   который
использует особенности утилиты GNU make. Makefile не очень сложный,  и
Вы вероятно захотите попытаться понять его. Все правила  определены  в
файле makefile.inc. Makefile включает этот файл в себя и  не  содержит
любой ОС и ЦПУ специфичной информации. Всё, что Вы должны  сделать  во
время портирования поекта это добавить ОС и ЦПУ специфичные  секции  в
makefile.inc используя как шаблон предыдущие объявления таких  секций.
После этого Вы можете изменить значения TARGET_OS и  TAGET_PLATFORM  в
нужные Вам.

4. Примечания по оптимизации
============================

   Задача оптимизация любой программы требует отдельного  освещения  и
индивидуального подхода. У каждой программы  есть  свои  тонкие  места
которые требуют  специальной оптимизации для  качественного  ускорения
всего проекта.
   Поскольку biew - это интерактивная программа,  то  для  него  таким
местом является обмен информацией с  консолью.  Хотя  в  любом  случае
лучшим средством поиска таких мест, в каждом отдельном случае, является
профилировщик,  но  уже  сегодня  известны  функции  при   оптимизации
которых можно серьёзно ускорить проект:
- __MsGetPos
- __vioGet(Set)CursorPos     (кешируется      при        использовании
  twGet(Set)CursorPos)
   Таким образом лучшей реализацией этих и других функций  для  работы
с консолью будет реализация  внутренних  флажков,  сигнализирующих  об
изменении состояния  наблюдаемого  (этой  функцией)  объекта,  которые
изменяют своё состояние асинхронно.
   При  оптимизации   не   интерактивных   функций   программы   нужны
специальные измерения профилировщика.
   См. также п. 4.3

4.1 Несколько слов о соглашениях вызовов функций
------------------------------------------------

   Почему то случилось так, что основная масса программистов  считает,
что удел языка C это cdecl вызовы  функций.  Совершенно  понятно,  что
K&R  создали язык C на заре развития компьютерной индустрии. В  основе
принятых   соглашений   о   вызовах   функций     лежит     реализация
переменного  числа  аргументов  (...).   Но, во  первых  -  процентное
соотношение таких функций на программу крайне мало  и  во  вторых  эта
была первая  редакция  стандарта,  что  в  дальнейшем  было  правильно
расширено  комитетов  ANSI  до  модификаторов  вызова.   Я   не   хочу
рассматривать эти вопросы в отношении не Intel архитектур, может  быть
там и существуют свои  особенности,  хотя  я  убеждён, что  и  на  тех
платформах  можно  применить  более  выгодные   вызовы   функций.   Но
поскольку, на момент написания, этого документа единственными рабочими
платформами для программы остаются ia16 и ia32 архитектуры, то  такого
рода оптимизация не лишена смысла. Конечно, мне можно возразить, что с
появлением   в   современных   процессорах   предсказания   ветвления,
конвейеров и т.д. эффект от подобных изменений теряется, но  во-первых
это ещё не означает, что  программа  не  будет  запускаться  на  более
ранних моделях процессоров.  Во-вторых  уменьшение  размера  кода  (за
счёт  подобной  оптимизации) всегда   приводит   к   более   выгодному
использованию  процессорных  кешей  и  в  третьих - ещё  не факт,  что
конвейеры и  предсказания  сводят  на  ноль  эффект  оптимизации,  они
минимизируют влияние cdecl но не так, что разницы не остаётся совсем.
   Как бы там  не  было,  в  коде  определён  макрос  __FASTCALL__,  с
помощью которого модифицирована большая часть проекта. В принципе  его
можно  переопределить  в  любое  значение,  но  при  разработке  новых
функций будет правильно использовать его.

Что сказано о соглашениях вызовов в руководстве Watcom:

__cdecl:
Определяет соглашение вызова используемое Microsoft компиляторами.

Примечания:
1.      Всем  символам  предшествует  символ  подчеркивания.
2.      Аргументы передаются  через  стек  справа  на  лево.  Так  что
        последний  аргумент  заносится  в  стек   первым.   Вызывающая
        процедура должна очистить стек от аргументов.
3.      Значения  с  плавающей  точкой  возвращаются   также,  как   и
        структуры. Когда возвращается структура, вызываемая  процедура
        возвращает  указатель  в  регистре  AX/EAX   на   возвращаемое
        значание которое записано в сегменте данных (DGROUP).
        (NK: В 32-х  разрядной  версии  значения  с  плавающей  точкой
             возвращаются в 80x87 регистре ST(0)).
4.      Для 16-ти разрядных компиляторов, регистры AX, BX, CX и DX,  а
        также   сегментный   регистр   ES     не     сохраняются     и
        восстанавливаются после вызова.
5.      Для 32-х разрядных компиляторов, регистры EAX, ECX  и  EDX  не
        сохраняются и восстанавливаются после вызова.

__stdcall:
(только для  32-х  разрядных)  Ключевое  слово  __stdcall  может  быть
использовано с определением функций,  и  обозначает  что  должно  быть
использовано 32-х разрядное соглашение о вызовах Win32.

Примечание:
1.      Всем символам предшествует символ подчеркивания.
2.      Все символы - символы языка  C  (extern  "C"  символы  в  C++)
        имеют суффикс "@nnn" где "nnn" это сумма  размеров  аргументов
        (каждый размер округлён  в  большую  сторону  до  кратности  4
        байтам так что char и short  имеют  размер  4).  Когда  список
        аргументов содержит "...", то "@nnn" суффикс опускается.
3.      Аргументы передаются  через  стек  справа  на  лево.  Так  что
        последний  аргумент  заносится  в  стек   первым.   Вызываемая
        процедура должна очистить стек от аргументов.
4.      Когда вовращается структура,  вызывающая  процедура  размещает
        пространство  в   стеке.   Адрес   размещённого   пространства
        передаётся  через  стек  непосредственно   перед   инструкцией
        вызова функции. При  воврате  из  вызова,  регистр  EAX  будет
        содержать адрес  размещённого  пространства  для  вовращаемого
        значения. Значение с  плавающей  точкой  вовращается  в  80x87
        регистре ST(0).
5.      Регистры EAX, ECX и EDX  не  сохраняются  и  восстанавливаются
        после вызова.

__syscall:
(только для  32-х  разрядных)  Ключевое  слово  __syscall  может  быть
использовано с определением функции,  и  обозначает  что  должно  быть
использовано 32-х разрядное соглашение о вызовах  совместимое  с  32-х
разрядным инерфейсом OS/2.

Примечание:
1.      Символы имен не модифицируются, так  что,  они  не  украшаются
        начальными или конечными подчёркиваниями.
2.      Аргументы передаются  через  стек  справа  на  лево.  Так  что
        последний  аргумент  заносится  в  стек   первым.   Вызывающая
        процедура должна очистить стек от аргументов.
3.      Когда вовращается структура,  вызывающая  процедура  размещает
        пространство  в   стеке.   Адрес   размещённого   пространства
        передаётся  через  стек  непосредственно   перед   инструкцией
        вызова функции. При  воврате  из  вызова,  регистр  EAX  будет
        содержать адрес  размещённого  пространства  для  вовращаемого
        значения. Значение с  плавающей  точкой  вовращается  в  80x87
        регистре ST(0).
4.      Регистры EAX, ECX и EDX  не  сохраняются  и  восстанавливаются
        после вызова.

__pascal:
(только  для  16-ти  разрядных)  Определяет   соглашения   о   вызовах
используемое в OS/2 1.x и функциях Windows 3.x API.

Примечание:
1.      Все символы преобразуются в верхний регистр.
2.      Аргументы передаются через стек в обратном порядке.  Так  что,
        первый   аргумент   передаётся   первым,   второй   передаётся
        следующим, и т.д. Вызываемая процедура  должна  очистить  стек
        от аргументов.
3.      Значения  с  плавающей  точкой  возвращаются   также   как   и
        структуры. Когда вовращается структура,  вызывающая  процедура
        размещает   пространство   в    стеке.   Адрес    размещённого
        пространства  передаётся  через  стек  непосредственно   перед
        инструкцией вызова функции. При воврате из вызова, регистр  AX
        будет   содержать   адрес   размещённого   пространства    для
        вовращаемого значения.
4.      Регистры AX, BX, CX и DX, а также  сегментный  регистр  ES  не
        сохраняются и восстанавливаются после вызова.

Примечание автора:

__fastcall:
В разных компиляторах реализован по разному.

Примечание:
1.      Имена функций или не модифицируются или  обрамляются  конечным
        подчёркиванием.
2.      Аргументы передаются через  регистры  (E)AX,  (E)BX,  (E)CX  и
        (E)DX. Если  регистров  не  хватает  то  оставшиеся  аргументы
        передаются через  стек  справа  на  лево.  Так  что  последний
        аргумент заносится в стек первым. Вызываемая процедура  должна
        очистить стек от аргументов. В некоторых реализациях  значения
        с   плавающей   точкой   также   передаются   через   регистры
        сопроцессора 80x87 (ST(0)-ST(2(5))).
3.      В  некоторых  реализациях  небольшие  структуры   возвращаются
        через  регистры  процессора.  Значение  с   плавающей   точкой
        возвращается  в  80x87  регистре  ST(0).  Когда   возвращается
        большая   структура,    вызывающая     процедура     размещает
        пространство  в   стеке.   Адрес   размещённого   пространства
        передаётся  через  стек  непосредственно   перед   инструкцией
        вызова функции. При возврате из вызова,  регистр  (E)AX  будет
        содержать адрес размещённого  пространства  для  возвращаемого
        значения.
4.      Все  регистры, кроме  регистров  через   которые  возвращаются
        значения,  сохраняются  и  не  требуют  восстановления   после
        вызова.

4.2. Примечания по оптимизации исходных текстов
-----------------------------------------------

   К приведенному ниже материалу можно относится по разному  -  как  к
руководству о том, как никогда не надо программировать или  же принять
его к действию. (Даже у меня, при  чтении  возникали  вопросы  типа  -
почему  это  работу  переложили   с   "плеч"   процессора   на   плечи
программиста). Но  так  или  иначе  -  это  оффициальные  рекомендации
ведущих производителей процессоров для персональных  компьютеров  и  с
ними крайне желательно ознакомиться.

Руководство по Intel P-III:            Руководство по Athlon-K7:

(System Programming Guide, Order       (Publication # 22007 Rev: D
Number 243192 (pages 443 and           Issue Date: August 1999 (pages 21 and
below)):                               below)):

 [14.1.1. Основные руководящие         Оптимизация на уровне языка "C":
 принципы оптимизации кода]

Пишите код, который может быть         Эта глава детализирует методы програм-
оптимизирован компилятором.            мирования на языке "C" для оптимизации
Пример:                                кода под AMD Athlon процессор:
*******************************************************************************
- Минимизируйте использование          Избегайте частых разыменований указателей
  глобальных переменных, указателей    переданных как аргументы внутри функции.
  и сложных выражений управляющих      Поскольку компилятор не знает о существу-
  логикой программы.                   ющих связях между указателями, такое ра-
                                       зыменование не может быть им оптимизи-
                                       ровано. Это предотвращает хранение данных
                                       в регистрах и значительно увеличивает
                                       обмен данных с памятью.

- Не используйте модификатор           --------------[ N/A ]----------------
  "register".

- Используйте модификатор "const".     Используйте классификатор типа "const"
                                       настолько насколько возможно.
                                       Эта оптимизация делает код более
                                       устойчивым и может произвести к наивысше
                                       оптимизированному коду предоставляя
                                       дополнительную информацию доступную
                                       компилятору. К примеру, стандарт языка
                                       "C" разрешает компилятору не размещать
                                       пространство под объект, который объявлен
                                       как "const", если его адрес никогда не
                                       берётся.

- Не делайте преобразований типов.     --------------[ N/A ]----------------

- Не производите косвенных вызовов.    --------------[ N/A ]----------------

- Знаковые расширения обычно очень     Если возможно, используйте беззнаковые
  доргостоящие.                        целочисленные типы в предпочтение зна-
                                       ковым. Беззнаковые типы сообщают
                                       компилятору, что данные не могут
                                       отрицательными, что допускает некоторую
                                       оптимизацию не возможную над знаковыми
                                       и потенциально отрицательными данными.

--------------[ N/A ]----------------  Оптимизируйте выражения "switch":
                                       рекомендуется сортировать "case" выра-
                                       жения в соответсвии с вероятностью их
                                       появления, ставя наиболее вероятные
                                       первыми.
                                        int days_in_month, short_months,
                                            normal_months, long_months;
                                         switch (days_in_month) {
                                            case 31: long_months++; break;
                                            case 30: normal_months++; break;
                                            case 28:
                                            case 29: short_months++; break;
                                            default: printf ("month has fewer"
                                            "than 28 or more than 31 days\n");
                                         }

--------------[ N/A ]----------------  Объявляйте локальные функции как "static"
                                       Функции, которые не используются за
                                       пределами файла, в котором они определены
                                       всегда должны объявляться статическими,
                                       что форсирует внутреннюю сборку. В про-
                                       тивном случае такие функции умеют внеш-
                                       нюю сборку по умолчанию, что способно
                                       сдерживать некоторую оптимизацию у неко-
                                       торых копмиляторах к примеру, агрессивное
                                       встраивание функций.

--------------[ N/A ]----------------  Используйте прототипы для всех функций:
                                       Прототипы могут сообщить дополнительную
                                       информацию компилятору что может допус-
                                       тить более агресивную оптимизацию.

- Для лучшей производительности,      Обсуждение струтурных компонент языка "C"
  убедитесь, что данные структур и     - Сортируйте члены структур согласно их
  массивов, которые больше 32 байт,      базовым типам, объявляя члены с боль-
  выравнены на границу 32 байта, и       шими базовыми типами впереди членов с
  что используемый способ доступа к      меньшими размерами базовых типов.
  данным не нарушает правил выра-      - Выравнивайте структуру кратно большему
  внивания.                              размеру базового типа любого члена:
                                         struct {
                                           double x;
                                           long k;
                                           char a[5];
                                           char pad[7]; } baz;

- ВЫРАВНИВАНИЕ ДАННЫХ В СТЕКЕ          Сортируйте локальные переменные в соот-
  Используйте статические переменные   ветствии с их базовыми типами:
  вместо динамических (стековых) пе-   Когда компилятор размещает локальные пе-
  ременных. На процессорах Pentium,    ременные в том же порядке в каком они
  доступ к 64-х битовым переменным,    объявлены в исходном коде, может быть
  которые не выравнены на границу 8    полезно объявить локальные переменные в
  байт будет стоить 3 цикла процессора.таком стиле, чтобы переменные с большим
  На процессорах семейства P6, такой   размером базового типа объявлялись впере-
  доступ к 64-х битовым переменным     ди переменных с меньшим размером базового
  вызовет перезагрузку кеша данных.    типа:
                                        double z[3];
                                        double x, y;
                                        long foo, bar;
                                        float baz;
                                        short ga, gu, gi;

- Используйте минимальные размеры для  Используйте 32-х битные типы данных для
  целочисленных типов и типов с пла-   целочисленных вычислений. Компиляторы ре-
  eвающей точкой чтобы разрешить SIMD  ализованы по разному но обычно поддержи-
  паралелизм.                          вают следующие типы данных: int, signed,
                                       signed int, unsigned, unsigned int, long,
                                       signed long, long int, signed long int,
                                       unsigned long и unsigned long int.

--------------[ N/A ]----------------  Избегайте ненужных целочисленных делений:
                                       Целочисленное деление медленно для всех
                                       целочисленных арифметических операций и
                                       должно избегаться везде, где возможно.

- Разворачивайте все короткие          Полное разворачивание циклов снижает наг-
  циклы которые выполняются менее, чем рузку на регистры удалением счетчика цик-
  за 2 такта затраченных в цикле.      ла. Для полного раворачивания цикла, уда-
                                       лите управление циклом и размножьте тело
                                       цикла N раз. В дополнение, полное разво-
                                       рачивание циклов увеличивает возможности
                                       планирования. Только разворачивание очень
                                       больших цилов кода может привести к не-
                                       эффективному использованию кеша инструк-
                                       ций L1.

--------------[ N/A ]----------------  Всегда встраивайте функции которые меньше
                                       25 машинных инструкций

- Уделите внимание алгоритму предска-  Размещайте адреса ветвлений за или перед
  зания ветвлений целевого процессора. началом окна кода выравненнного на 16
  Эта оптимизация особенно важна для   байт. Эта техника помогает максимализиро-
  семейства P6 процессоров.            вать число инструкций которыми заполнена
  Код, оптимизированный под способ-    очередь инструкций.
  ность предсказаний затратит меньше
  тактов выборки инструкций.

- Воспользуйтесь преимуществом SIMD    Используйте 3DNow! инструкции:
  возможностей MMXT технологии и       Если не требуется повышенная точность
  её расширениями.                     в противном случае, выполняйте вычисления
                                       с плавающей точкой используя 3DNow! инст-
                                       рукции вместо x87 инструкций. SIMD это
                                       природа 3DNow! инструкций достигает
                                       удвоенной производительности, которая мо-
                                       жет достигаться через x87 инструкции.
                                       3DNow! инструкции также обеспечивают пло-
                                       ский файл регистров вместо стекового метода
                                       x87 инструкций.

- Избегайте частичного проставания     Избегайте частичного чтения и записи:
  регистров:                           В порядке поддержки частичной записи в
  Когда записываются большие (32-х     регистр, в процессоре AMD Athlon реали-
  разрядные) регистры общего           зована схема слияния данных в исполняющем
  назначения непосредственно после     ядре. В исполнительном блоке, инструкции
  отработки (8- или 16-ти разрядных)   записи частичного регистра объединяет
  регистров, содержащихся в больших    изменённая часть с текущим состоянием
  регистрах они простаивают пока       остатка регистра. Следоватиельно, зависи-
  идет запись (минимум 7 тактов).      мости железа потенциально могут вызвать
  Пример:                              ложные зависимости на самой последней
    MOV AX, 8                          инструкции которая пишет в любую часть
    ADD ECX, EAX ;Частичное проста-    регистра.
                 ;ивания происходит    Пример 1 (Избегайте):
                 ;при доступе к EAX      MOV AL, 10 ;inst 1
  Здесь, первая инструкция перемещ.      MOV AH, 12 ;inst 2.
  число 8 в малый регистр AX.          Inst 2 ложно зависит от inst 1.
  Следующая имеет доступ с последу-    Inst 2 объединяет новый AH с текущим
  ющим частичным простаиванием         состоянием EAX. В этом коде значение
  регистра.                            регистра EAX определяется inst 1. В
                                       дополнение, инструкция которая имеет за-
  Pentium R и Intel486T процессоры     висимость по чтению из любой части задан-
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^     ного регистра имеет зависимости по чтению
  не генерируют это простаивание.      от самой последней инструкции которая мо-
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^      дифицирует любую часть того же регистра.
                                       Пример 2 (Избегайте):
                                         MOV BX, 12h ;inst 1
                                         MOV BL, DL  ;inst 2
                                         MOV BH, CL  ;inst 3
                                         MOV AL, BL  ;inst 4
                                       Inst 2 ложно зависит от от завершения
                                       inst 1. Inst 3, ложно зависит от за-
                                       вершения inst 2. Inst 4, зависит от
                                       завершения inst 2.

- Выравнивайте все данные.             Избегайте несовпадения размеров в памяти:
 - Выравн 8-битн данные на любую гр.   - Выравн 8-битн данные на любую границу
 - Выравн 16-битн данные на границу    - Обращение размером WORD выравнивайте
   4-байт.                               по адресу делящимся на 2.
 - Вырван 32-битн данные на границу    - Обращение размером DWORD выравнивайте
   кратную 4.                            по адресу делящимся на 4.
 - Выравн 64-битн данные на границу    - Обращение размером QWORD выравнивайте
   кратную 8.                            по адресу делящимся на 8.
 - Выравн 80-битн и 128-битн данные    - Обращение размером TBYTE выравнивайте
   на 128-битн границу (то есть,         по адресу делящимся на 16.
   любую границу кратную 16 ).

 - Вход в цикл должен быть выравнен    --------------[ N/A ]----------------
   на границу 16-байт когда он рас-
   положен менее чем на 8 байт от
   этой границы.

 - Метка следующая за условным         --------------[ N/A ]----------------
   переходом не должна быть выровнена

 - Метка следующая за безусловным       --------------[ N/A ]----------------
   переходом или вызов функции
   должен быть выравнен на 16-байт
   если он расположен менее чем на
   8 байт от границы.

 Штрафы выравнивания:                  Избегайте невыравненных ссылок на данные.
 - На процессорах Pentium R невырав-   невыравненные операции записи или чтения
   ненный доступ стоит 3 такта.        вызываеют минимум 1-тактовый штраф в
 - На процессорах семейства P6,        AMD Athlon процессоре на перезагрузку
   невыравненный доступ который пере-  конвейера.
   секает границу линий кеша стоит от
   6 до 9 тактов.
 - На процессорах семейства P6,
   невыравненный доступ который вы-
   зывает разрыв данных кеша останав-
   ливает процессор. Разрыв кеша данных
   это доступ к памяти, который пересе-
   кает 32-байтные границы линий кеша.

- Динамический захват памяти посредс-  Соглашения динамического захвата памяти:
  твом MALLOC: при использовании       Динамический захват памяти (malloc в
  MALLOC проверьте что компилятор вы-  языке "C") должен всегда возвращать ука-
  равнял doubleword или quadword зна-  затель выравненный для наибольшего базо-
  чения на границу 8 байт. Если ком-   вого типа (quadword выравнивание). Где
  пилятор не поддерживает это выравн.  такое выравнивание не гарантируется,
  тогда используйте след. технику для  используйте технику показанную в сле-
  оптимального выполнения кода:        дующем фрагменте чтобы выравнять указа-,
                                       тель. Этот код полагает что указатель
  1. Захват. память равную размеру     может указывать на long. Пример:
     массива или структуры + 4 байта.
  2. Используйте "побитовое" И чтобы
     убедиться что массив выравнен,
     для примера:
       double a[5];                     double* p;
       double *p, *newp;                double* np;
       p = (double*)malloc              p = (double *)malloc
         ((sizeof(double)*5)+4)           (sizeof(double)*number_of_doubles+7L);
       newp = (p+4) & (-7)              np = (double *)((((long)(p))+7L)&(8L));

- Организуйте код минимизирующий       !!!!!!!! [SAME ] !!!!!!!!!!!!!!!!!!!!!
  промахи кеша инструкций и оптими-
  зированный для предвыборки.

- Избегайте любых префиксов инструкций --------------[ N/A ]----------------
  кроме 0FH.

- Используйте программный конвейер.    !!!!!!!! [SAME ] !!!!!!!!!!!!!!!!!!!!!

- Всегда делайте парными CALL и RET    --------------[ N/A ]----------------
  инструкции.

- Избегайте самомодифицирующегося      !!!!!!!! [SAME ] !!!!!!!!!!!!!!!!!!!!!
  кода.

- Не размещайте данные в сегменте      !!!!!!!! [SAME ] !!!!!!!!!!!!!!!!!!!!!
  кода.

- Избегайте инструкций которые содер-  Используйте инструкции короткой длины:
  жат 4 или более микроопераций или    Ассемблеры и компиляторы должны генери-
  длинее 7 байт. Если возможно         ровать по возможности плотный код для
  ^^^^^^^^^^^^^^                       оптимизации использования I-Cache и
  используйте инструкции которые тре-  увеличения средней степени декодирования.
  буют 1 микрооперацию.                Когда возможно используйте инструкции
  Pentium R процессор без MMXT         короткой длины. Использование коротких
  технлогии не выполняет набор парных  инструкций увеличивает число инструкций,
  инструкций если в нем есть инстр.    которыми может быть заполнена очередь
  длинее 7 байт;                       инструкций.
  Pentium R процессор с MMXT           Избегайте:
  технологией не выполняет набор       81 C3 FB FF FF FF:  add ebx, -5
  парных инструкций если первая        Предпочтительно:
  инструкция длиннее 11 байт           83 C3 FB:           add ebx, -5
  или вторая инструкция длиннее
  7 байт. Префиксы не учитываются.
  ^^^^^^^
  Процессор семейства P6 имеет 3
  декодера для перевода Intel
  Architecture макро инструкций в
  микро операции (микрооперации, также
  называют "uops"). Ограниечения
  декодера следующие:
  Первый декодер (декодер 0) может
  декодировать инструкции до 7 байт в
                          ^^^^^^^^^
  длину и до 4 микроопераций за один
  такт. Вторые два декодера (декодеры
  1 и 2) могут декодировать инструкции,
  которые являются 1 микрооперационными
  инструкциями, и эти инструкции также
  декодируются за 1 такт.

  Так, для лучшей производительности на
  всех процессорах Intel, используйте
  простые инструкции, которые меньше 8
  байт в длину.

 [14.1.2. Руководящие принципы оптими-
 зации MMXT кода]

- Не перемешивайте MMXT инструкции     Нет штрафа на переключение между
  и инструкции с плавающей точкой.     x87 FPU и 3DNow!/MMX инструкциями
                                       в AMD Athlon процессоре.

- Используйте opcode reg, mem формат   Избегайте адресов генерирующих блокировки:
  инструкций везде где возможно. Этот  Намного выгоднее сначала рассчитать и
  формат помогает освободить регистры  сохранить сложные адреса и использовать
  и уменьшает такты без генерации      их перед циклами записи и чтения
  ненужных загрузок.                   требующих разрешения длинных цепочек
                                       зависимостей в порядке генерации их
                                       адресов.

- Используйте EMMS инструкцию в конце  FEMMS инструкция дожна быть использована
  всех MMXT секций кода, за которыми   для гарантии того, что этот же код будет
  произойдёт переход на вычисления с   оптимально работать на AMD-K6 процессоре.
  плавующей точкой.                    FEMMS инструкция поддерживается для сов-
                                       местимости с AMD-K6 процессором, и явля-
                                       ется псевдонимом EMMS инструкции.

 [14.1.3. Руководящие принципы опти-
 мизации кода с плавающей точкой]

- Поймите, как компилятор генерирует   - Используйте умножение чаще, чем деление
  код с плавающей точкой. Посмотрите   - Используйте FFREEP чтобы вытолкнуть
  ассемблерный дамп и найдите, какие     один регистр из стека FPU
  преобразования уже выполнены над     - Для переходов зависящих от результатов
  программой. Изучите вложенные циклы    сравнения чисел с плавающей точкой, ис-
  в приложении, которые составляют ос-   пользуйте FCOMI/FCOMIP/FUCOMI/FUCOMIP
  новное время выполнения. Определите,   инструкции. Эти инструкции намного быс-
  почему компилятор не создал наибыс-    трее классического метода - FSTSW, так
  трейший код. Для примера, взгляните    как FSTSW, по существу, последовательная
  на зависимости, которые могут быть     инструкция на AMD Athlon процессорах.
  разрешены упорядочиванием кода.        Когда FSTSW не может быть избегнута
                                         (к примеру, совместимость кода со ста-
                                         рыми процессорами), не должно быть дру-
                                         гих инструкций FPU между FCOM[P],
                                         FICOM[P], FUCOM[P], или FTST и за-
                                         висящих от FSTSW. Эта оптимизация до-
                                         пускает использование быстрого упрежда-
                                         ющего механизма для условного кода FPU
                                         внутри AMD Athlon процессора и увеличи-
                                         вает производительность.

- Посмотрите и исправьте известные     Убедитесь что все данные FPU выравнены:
  ситуации преводящие к замедлению
  выполнения кода с плавающей точкой:
  - Большой обмен с памятью            Невыравненное обращение к памяти уменьша-
                                       ет пропускную способность шины памяти.
  - Плохая загрузка кеша.
  - Долгое время ожидания арифметиче-
    ских операций с плавающей точкой.

- Не используйте большую точность, чем Избегайте использования данных расширен-
  это необходимо. Single precision     ной точности: Храните данные как single-
  (32-bits) быстрее на некоторых       precision или double-precision. Загрузка
  операциях и поглощает только поло-   и сохранение extended-precision данных
  вину памяти, чем double precision    сравнительно медлененны.
  (64-bits) или double extended
  (80-bits).

- Используйте библиотеки которые обес- Сократите преобразования из плавающей
  печивают быструю математику в цело-  точки в целочисленные значения.
  численных программах. Многие библио-
  течные функции не работают как это
  необходимо.

- Убедитесь где это возможно что вы-    --------------[ N/A ]----------------
  числения не выходят из нормальных
  пределов. Out-of-range числа при-
  водят к серьёзным потерям.

- Спланируйте код на ассемблере ис-    Используйте FXCH инструкцию чаще, чем
  пользуя FXCH инструкции. Когда воз-  пару FST/FLD:
  можно, разверните циклы и конвейе-   Использование FXCH предпочтительнее перед
  ризирйте код.                        FST/FLD парами, даже если FST/FLD пара
                                       работает с регистрами. FST/FLD пара до-
                                       бавляет два такта ожидания, состоит из
                                       двух операций.

- Преобразуйте код, чтобы улучшить ша- --------------[ N/A ]----------------
  блоны доступа к памяти. Используйте
  слияние циклов или сжатие чтобы со-
  хранить так много вычислений в кеше,
  как только возможно.

- Разорвите цепочки зависимостей.      --------------[ N/A ]----------------

 [14.6.3. Эффекты распределения записи]
Семейство процессоров P6 имеет "write  --------------[ N/A ]----------------
allocate by read-for-ownership" кеш,
тогда как Pentium R процессор имеет
"no-write-allocate; write through on
write miss" кеш.

 boolean array[max];
  /* 1 "boolean". В этом примере
     есть массив типа char. Здесь,
     было бы лучше использовать массив
     "boolean" как битовый массив,
     тем самым уменьшается размер мас-
     сива, который в свою очередь умень-
     шает число предвыборок кеша. */
 for(i=2;i<max;i++) {
   array[i] = 1;
 }
  for(i=2;i<max;i++) {
   if( array[i] ) {
    for(j=2;j<max;j+=i) {
     if( array[j] != 0 ) {
       array[j] = 0;
/* проверьте если величина уже 0.
если значение уже нулевое до записи
(как показано в примере), тем самым
уменьшается число записей в память
(грязные линии кеша) */
     }
    }
   }
  }

4.3. Тесты производительности
=============================

   С началом разработки ветки 5.3 программы, я  поставил  перед  собой
задачу, по  возможности, ускорить  проект.  Мне   пришлось   выполнить
достаточно много различных тестов в поисках тонких мест программы и  в
результате появилась приведённая ниже сравнительная таблица.

Итак ТЕСТ:
   Все   замеры   проводились   на   одном   и   том   же   компьютере
(AMDK6-200/128Mb). Меня интересовали не  абсолютные,  а  относительные
цифры. (Ещё лучше было-бы проводить  подобные  тесты  на  i386-40,  но
возможности найти такие раритеты у меня не оказалось).
   В  качестве  теста  я  выбрал  файл  kernel32.dll   и   использовал
следующие режимы дизассемблирования: Reference  prediction  (Ctrl-F8),
Local offsets (Ctrl-F6), далее Save as...(Shift-F10) режим  ассемблера
(F2), put structures (F4)

+-----------------------------------------------------------------+
| Операционка           | режим 5.2.0 |     MMF    | MMF + ^Break |
+-----------------------+-------------+------------+--------------+
| Linux-2.2.17-pre.14   | 0 m 58 sec  | 0 m 14 sec | 0 m 07 sec   |
| WinNT4.0+SP4          | 1 m 07 sec  | 0 m 28 sec | 0 m 07 sec   |
| OS/2(WSeB+fp1+UNI_060)| 4 m 57 sec  | 3 m 42 sec | 0 m 08(19)sec|
| DOS32 под WinNT       | 2 m 30 sec  | N/A        | N/A          |
| DOS32 под OS/2        | 3 m 13 sec  | N/A        | N/A          |
+-----------------------------------------------------------------+
Примечание:
здесь использованы следующие сокращения:
режим 5.2.0 - режим существующий в версии 5.2.0
MMF  -        Memory Mapped Files, техника  работы  с  файлом,  как  с
              обыкновенной памятью (RAM) (Известна также как mmap).
^Break      - В версии 5.3  реализована  новая  схема  опроса  нажатия
              пользователем  клавиш.   (В  5.2.0   для   того,   чтобы
              прекратить   операцию,   например,  прекратить   процесс
              дампирования, пользователь  должен  был  нажать  клавишу
              Escape, в  версии  5.3  Escape  заменена  на  комбинацию
              Ctrl-Break, поскольку в большинстве операционных  систем
              этот сигнал посылается  в  программу  ассинхронно, а  не
              через функции работы с клавиатурой).

Немного о  цифрах:
   Последнее  место  по  производительности  до   выхода   5.3.0-pre.2
занимала OS/2 версия программы, но с реализацией новых технологий  она
заняла достойное место по сравнительной  производительности.  Цифра  в
скобках указывает время работы программы при  первом  запуске.  Что  я
здесь имею ввиду, под певрым запуском  -  не  важно  сколько  работает
система важно что программа запущена впервые. Проблема в том, что  при
втором запуске запись идёт в файл уже находящийся в кеши.  Эти  8  sec
интересны только с точки зрения замера чистой  производительности,  на
практике же все будут пользоваться программой для совершения  операции
только однажды.
   Выводы: Несмотря на то, что выше очень много слов сказано про  OS/2
(хотя на сегодняшний день она и  не  представляет  собой  сколь-нибудь
серьёзный продукт), пример с этой  системой  говорит  о  том,  что  не
всегда достаточно оптимизировать только один блок (очевидно  требующий
оптимизации). В данном случае эффект был достигнут  после  оптимизации
сразу двух мест в логике, каждое из которых по отдельности  не  давало
такого сильного эффекта (в 37 раз по крайним значениям).
   При портировании программы под другие операционки, необходимо иметь
ввиду, что все функции,  находящиеся  в   системно-зависмых  каталогах
необходимо   разрабатывать   очень   тщательно   и   не   пренебрегать
значимостью каждой из них. (Например, эмулировать логику ассинхронного
сигнала Сtrl-Break через  обращение  к  функциям  клавиатуры  и  т.д.)
Проект от этого может качественно проиграть в производительности.

   НЕКОТОРЫЕ  ЗАМЕЧАНИЯ  ПО  GCC:  Этот  же  тест  я  использую,   при
внедрении fastcall технологии,  но  пока  на  сегодняшний  день  дампы
одного и  того  же  файла  получаются  разные  при  fastcall  и  cdecl
оптимизации проекта. Причём,  cdecl   оптимизированный  проект  выдаёт
правильные  дампы  файла   (такие   же,  как   и   собранный   другими
компиляторами). Пока я склонен относить это к ошибкам  gcc,  поскольку
никаких других идей у меня нет.

5. Окончание
============

  Это - всё!!!
