Ускорение форматных преобразований в системах реального времени, реализованных на языке PowerBasic для i386+ (X)

А.М.Терентьев

IBM PC сравнительно редко избирается для решения задач обработки данных в реальном времени. Огромное большинство созданных приложений либо ориентировано на интерактивную обработку данных (все приложения, включенные в Microsoft Office), либо, в крайнем случае, снабжаются макросами или скриптами автоматического выполнения, причем задача скорейшего их выполнения в смысле соответствия асинхронным процессам попросту не ставится. Соответственно этому, многочисленные развивающиеся программные средства создания программных приложений специально не ориентированы на скорейшее выполнение операций, компиляторы с языков высокого уровня известных коммерческих фирм часто функционально не рассчитаны на задачи реального времени, а входящие в их состав подключаемые библиотеки не оптимизированы по коду. На практике, рекомендации по обеспечению задач реального времени сводятся к повышению мощности процессора.

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

Типичным представителем описанного выше подхода является алгоритмический язык PowerBASIC [1-2], развиваемый «PowerBASIC Inc.» с 1989 г. С 1993 г. появилась весьма удобная версия 3.00 компилятора, с 1995 – полнофункциональная версия 3.20 под MS-DOS, с 2000 г. поддерживается версия PowerBASIC 32-bit DLL Compiler, а с 2002 г. – PowerBASIC 32-bit GUI Compiler. Особенностью всех указанных версий является наследование известного TurboBASIC фирмы «Borland International» (1987) по синтаксису и семантике, очень быстрые алгоритмы компиляции, принцип полуавтоматического подключения библиотек системных функций и процедур, постоянная работа над совершенствованием среды разработчика. Такая длительная (свыше 20 лет!) забота о классе «своих» пользователей встречается весьма редко и заслуживает высокой оценки.

Однако, практически всем рассмотренным (после Borland) версиям компиляторов свойственен ряд типичных программистских недочетов. Среди них – возложение функций по реструктуризации внутренних буферов переменных, в особенности текстовых, и «уборке мусора» на аппаратное прерывание по таймеру INT08; отсутствие полновесных возможностей доступа к памяти переменных (типа оператора EQUIVALENCE), не всегда корректная работа с целочисленными словными переменными и неоптимальность форматных преобразований. Все эти недостатки, кроме первого из названных, могут быть обойдены при практическом программировании. Ряд усилий предпринимает сама фирма – например, начиная с версии 3.20 компилятора для DOS, введены аппараты пойнтеров и flex-строк. Недочеты в «уборке мусора» в целом не влияют на производительность, хотя мешают разработкам систем реального времени. А вот последний из названных недостатков может серьезно снизить производительность таких систем.

Автору пришлось столкнуться с подобной проблемой при разработке средств аудита локальных и корпоративных вычислительных сетей в MS-DOS. Обрабатывающая программа, получая из сети пакеты информации асинхронно, обязана справляться с их обработкой, не допуская необработанных пакетов. При этом, после обработки каждого пакета корректируется ряд цифровых показателей на текстовом экране. Исследование временных затрат в рабочем цикле показало, что при средних скоростях 50-100 Кб/c (типовая скорость в малых и средних локальных сетях) отключение вывода на экран повышает скорость обработки принимаемых пакетов с 200-600 рабочих циклов в секунду до 2000-8000, в зависимости от типа процессора. Такие данные однозначно свидетельствуют о том, что преимущественные затраты времени в цикле обработки связаны с форматными преобразованиями в ходе вывода числовых значений в текстовом виде.

Автором была исследована возможность построения собственных программ форматных преобразований (из внутреннего вида в символьный) при следующих ограничениях:

- предельная разрядность числа заведомо известна;

- лидирующие нули заполняют всю длину результирующего поля.

Разумеется, при предложении пользователям того или иного компилятора фирмы не предоставляют сведений о характеристических признаках и методологии своих разработок. В связи с этим, кроме прочего, были исследованы некоторые операции языка, навлекшие подозрения относительно качества их реализации в компиляторах фирмы «PowerBASIC Inc». В этих целях проведены следующие исследования:

- определялась корректность предлагаемой фирмой-разработчиком замены оператора сложения в конструкциях вида C=C+I на оператор INCR для некоторых типов данных;

- определялась сравнительная скорость создания строковых элементов различных типов и ряда операций с ними;

- определялись сравнительные скорости исполнения циклов операций сравнения и предложенной фирмой-разработчиком заменяющей операции ARRAY SCAN по трем типам текстовых объектов языка PowerBASIC.

Все исследования выполнялись на 11 типах персональных компьютеров в среде MS-DOS 6.22 без файлов конфигурации. Замеры времени выполнены с помощью встроенного в синтаксис языка микротаймера, который, по заявлению разработчиков PowerBASIC, работает с точностью 20мс. Все проверки выполнялись в циклах, количество которых подобрано с целью получения удобных репрезентативных значений.


Исследования временных характеристик операторов сложения и INCR

Определялись времена исполнения операций C=C+I, INCR C,I, W=W+I и INCR W,I, где C – целочисленный операнд двойной точности (4 байта), I – целочисленный операнд обычной точности (2 байта), W – беззнаковый целый операнд обычной точности (2 байта). Выбраны фиксированные «средние» начальные значения операндов. Выполнялось по 2000 циклов соответствующих операций. Упрощенный фрагмент программы, исполняющий соответствующие циклы операций, представлен на рис. 12.


Рис. 12. Фрагмент программы с тестом времени исполнения операций сложения


Полученные результаты представлены в таблице 2. Легко видеть, что несмотря на известные несоответствия полученных данных скоростям процессора, оператор INCR явно реализован не лучшим образом, и рекомендуемое фирмой-производителем PowerBASIC его использование не может быть принято во внимание на рассматриваемых задачах.


Табл. 2. Замеры времен исполнения операций суммирования

Исследования работы со строковыми переменными

В рассматриваемой задаче сетевого мониторинга для быстрого определения источника пакета приходится сравнивать его MAC-адрес с хранящейся в памяти таблицей. В силу специфики программы наиболее экономное хранение этих данных исполняется в массивах текстовых переменных. Длина MAC-адреса в Ethernet - 6 байтов. В языке PowerBASIC определены три типа текстовых переменных: строки фиксированной длины, строки переменной длины и так называемые flex-строки (всегда фиксированной длины).

В этой связи основной интерес представляют операции присвоения значения текстовой переменной и исполнение поиска до первого совпадения. Стандартный метод поиска реализуется циклом FOR...NEXT, однако язык PowerBASIC имеет операторы обработки массивов, и в том числе оператор поиска в массиве по образцу ARRAY SCAN. В первых версиях программы наблюдающих станций для хранения таблицы MAC-адресов были использованы массивы текстовых строк фиксированной длины, а поиск реализовывался стандартным циклом FOR...NEXT с прерыванием цикла после нахождения. Однако, после успеха прототипа пилот-системы, при построении реальной программы для точного выбора соответствующей языковой поддержки следовало провести соответствующие тесты. Это и было сделано соответствующим блоком тестовой программы, аналогичным рассмотренному выше. Для заметного сравнения времен каждая операция исполнялась в цикле 200 раз. Соответствующий фрагмент тестовой программы приведен на рис. 13.


Рис. 13. Фрагмент программы проверки работы со строками и массивами строк


Полученные на той же группе ПК, что и вышеприведенные данные, результаты приведены в таблице 3.

Первые же замеры показали, что выбор фиксированных строк при реализации пилот-системы оказался наихудшим из возможных. Именно операция присваивания уже занимает заметно большее время для строк фиксированной длины, чем для строк переменной длины. Этот результат показывает, что практическая реализация аппарата поддержки текстовых строк фиксированной длины в компиляторе с языка PowerBASIC оставляет желать лучшего по ресурсным характеристикам. В то же время, неожиданным и приятным сюрпризом явился быстрый алгоритм реализации ARRAY SCAN, который давал хоть и несколько большее время на flex-строках (что вполне объяснимо каждому практическому программисту на Ассемблере), однако это единственное преимущество строк фиксированной длины не могло повлиять на выбор конструктивного элемента хранения MAC-адресов в пользу flex-строк.

Стоит обратить внимание на строку 6 рассматриваемой таблицы, соответствующую AMD-K6-2-300. Величина 1010 в шестой колонке отнюдь не является ошибкой эксперимента или неточностью. Выше уже говорилось о том, что результирующая программа, полученная после компиляции строк языка PowerBASIC, периодически исполняет процедуру «сборки мусора» именно в отношении текстовых переменных, причем этот процесс сделан неуправляемым для программиста. В рассматриваемый интервал как раз и попал цикл такой процедуры, что привело к несуразному значению микротаймера 1010 в этом месте (естественным было бы значение порядка 600-700).


Табл. 3. Замеры различных операций со строками и массивами строк разных типов

Исследования операций форматирования данных

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

За незначительным исключением, программа сетевого мониторинга выводит результаты в виде десятичных целых неотрицательных значений. Однако, ряд отладочных данных и часть оперативной информации (например, MAC-адреса) имеют вид 16-ричных чисел, без которых обойтись невозможно. Поэтому исследования форматных преобразований начаты именно с шестнадцатиричных форматов, которые могут быть сведены к двум основным: байтному и словному.

Синтаксис языка PowerBASIC предлагает стандартную функцию HEX(...) для получения 16-ричного вида числа. Однако, эта функция в зависимости от конкретного значения аргумента может иметь результат разной длины. Так, HEX$(13) есть 1 символ "D”, в то же время HEX(31313) есть 4 символа “7A51”. Как видим, лидирующие нули не выдаются, а лидирующие пробелы исключаются из вывода. Такое форматирование неудобно, а в ряде случаев (например, при выводе пословно MAC-адресов) недопустимо. На стадии пилот-программы соответствующие действия выполняла процедура-функция на языке PowerBASIC наподобие приведенной на рис. 14.


Рис. 14. Процедура форматирования слова в 4-символьный 16-ричный вид


В качестве замены двух процедур 16-ричного форматирования была написана программа на Макроассемблере, отдававшая результат вместо строки переменной длины во flex-строки с фиксированными длинами 2 и 4 для байта и слова соответственно. Операторы обращения к процедурам-функциям языка PowerBASIC были заменены обращениями к процедурам на Макроассемблере.

Тестовые исследования включали исполнение 1000 циклов каждого преобразования. Более того, замеры исполнялись несколько раз в ходе тестовой программы для исключения вынужденных ошибок вследствие непредсказуемой операции «сборки мусора» текстовых строк, а также для нивелирования различных результатов в серии (видимо, из-за таймерных прерываний, время исполнения которых зависит от абсолютного значения времени суток).

Результаты показаны в таблице на рис. 6 все для тех же 11 типов компьютеров. Вывод очевиден: заменой написанных на языке PowerBASIC процедур форматирования 16-ричных чисел оригинальным программированием на языке низкого уровня можно ускорить почти на порядок исполнение этих операций в результирующей программе.


Табл. 4. Времена форматирования байта и слова различными процедурами

После получения такого результата естественным следствием была попытка заменить и десятичные процедуры форматирования оригинальным программированием. Однако, для десятичного форматирования с заполнением лидирующими нулями язык PowerBASIC имеет специальный оператор USING(“##...#”,...). В целях полноты исследования следовало проверить, не является ли этот оператор более экономным по времени исполнения. Результаты (см. табл. 5) показали, что действительно применение этого оператора примерно в 2 раза более эффективно, чем указанная процедура-функция.

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


Табл. 5. Сравнение процедуры-функции форматирования с оператором USING$

Поэтому в целях исследования была написана соответствующая библиотека процедур на Макроассемблере (для получения OBJ-файла использован MASM версии 5.10 1998 г.). Библиотека включала несколько различных процедур для преобразования десятичных чисел из разных исходных типов данных на PowerBASIC в соответствующей длины текстовую строку. Тип входного данного каждой процедуры определялся реальными потребностями форматизации. Так, для длинных целочисленных величин, соответствовавших числу пакетов (принятому, отказанному и т.д.) задействован 7-символьный формат вывода; для числа байтов, представляющихся в основной программе сетевого мониторинга, предусмотрен 9-символьный формат. Эта же библиотека включала ранее написанные подпрограммы перевода байтов и слов в шестнадцатиричный вид, а также несколько вспомогательных процедур (в частности, для преобразования ip-адресов в вид их символьного представления).

Тестовая программа на PowerBASIC 3.00 сравнивала времена исполнения процедур-функций преобразования, написанных на языке PowerBASIC, с временами аналогичных преобразований с использованием созданной библиотеки процедур. Для получения значимых результатов сравнивались времена исполнения 1000 циклов тех и других преобразований. Каждое сравнение исполнялось дважды, чтобы компенсировать работу схемы «уборки мусора» внутри этих циклов. Для более ясного понимания техники замеров на рис. 15 приведен фрагмент тестовой программы для случая 5-символьных преобразований (однако, сохранены определения всех входящих в построенную библиотеку процедур на Макроассемблере).


Рис. 15. Фрагмент программы сравнения 5-символьных десятичных преобразований


В реальности, разумеется, одна программа содержала и все проверки, приведенные в предыдущих разделах статьи, и ряд проверок, обсуждаемых ниже, а также некоторые дополнительные действия, выходящие за рамки данной статьи. Все процедуры-функции преобразований (всего 8 штук), послужившие сравнением для написанных оригинальных, построены аналогично приведенной в конце рис. 15 процедуре FND5, отличаясь в основном типом входного аргумента (целое со знаком 2 байта, беззнаковое целое 2 байта, целое со знаком 4 байта и др.).


Рис. 16. Реальный файл отчета серии тестовых проверок на Pentium-MMX-200


Общий объем текста библиотеки форматных преобразований составил 260 строк Макроассемблера. Объем результирующего объектного файла – 874 байта. Все параметры предполагаются переданными по ссылке.

Для облегчения фиксации получаемых в ходе тестирования результатов, они, помимо экрана, дублировались также в выходной файл. Пример полной реальной выдачи тестовой программы приведен на рис. 16.

Небезынтересно, что из анализа таблиц 5-6 можно заключить, что результаты сильно зависят от линейки процессоров: AMD и Athlon, в противоположность предыдущим сравнениям, дают результаты хуже, чем Intel Pentium.

Сводка результатов замеров форматных преобразований в десятичный вид приведена в таблице 6. Видно, что во всех случаях процедура-функция на языке PowerBASIC более чем на порядок медленнее, чем аналогичная процедура на Макроассемблере. Вспоминая, что функция USING(...) всего лишь в два раза быстрее процедуры-функции, получаем, что соответствующая подпрограмма из созданной библиотеки более чем в 5 раз эффективнее непосредственного применения конструкций языка PowerBASIC.


Табл. 6. Времена процедур форматирования на PowerBASIC и п/п на MASM

В целом, налицо явное преимущество процессоров AMD-K2 и Duron над эквивалентными Celeron и даже Intel Pentium-III в задачах реального времени, решаемых в MS-DOS. К сожалению, и Athlon-1000 показывает явное преимущество перед Intel Pentium 4-1500.

Суммируя, можно сказать, что исследования показали весьма низкую эффективность реализации типовых конструкций языка PowerBASIC перед предложенными процедурами на Макроассемблере. Это тем более странно, если иметь в виду, что реализация типовых конструкций языка (в данном случае USING(...)) выполнена через присоединяемую при компиляции библиотеку типовых процедур. Вероятно, фирма-разработчик создавала свои типовые процедуры на языке более высокого уровня, нежели Ассемблер.

Предложенная библиотека процедур с успехом используется в актуальных версиях программ, работающих на наблюдающих станциях. Ее внедрение позволило существенно, более чем в 10 раз снизить ресурсные затраты на цикл обработки принимаемых сетевых пакетов в процессе сетевого мониторинга.


Литература
  1. Zale, Robert S. PowerBASIC Compiler, version 3. User’s Guide. — PowerBASIC, Inc. 316 Mid Valley Center. Carmel, CA 93923. — 335c.
  2. Zale, Robert S. PowerBASIC Compiler, version 3. Reference Guide. — PowerBASIC, Inc. 316 Mid Valley Center. Carmel, CA 93923. — 335c.

(X)Работа выполнена при финансовой поддержке РФФИ, проект N 04-07-90260в

Статья опубликована в 2004 г.