Усовершенствование компилятора. Тесты

Во время написания компилятора я совсем не задумывался об эффективности. Я не сделал ничего ни для увеличения скорости компиляции, ни для увеличения быстодействие кода, ни для уменьшения его размера. Тем не менее, компилятор уместился в шестидесяти четырех килобайтах, ста двадцати восьми килобайт ему было достаточно для работы (если объединить сегменты кода и стека), качество кода было несколько хуже, чем у ранних версий Turbo Pascal (что я выяснил лишь недавно). Самое слабое место - скорость компиляции - первая версия Context'а была в десятки раз медленнее Turbo Pascal. Но тогда факт получения работающей системы я посчитал вполне достаточным, каких-либо оценок и сравнений не делал, да и программа на языке Context была всего одна - маленький текстовый редактор edit64k (программы типа Hello, World! я не упоминаю).

Многое можно было сделать лучше, например:

Это не оптимизация, а всего лишь небольшие усовершенствования простого генератора кода. Нельзя сказать, что все это дает очень много, но кое-что дает.

Ниже приведены результаты некоторых тестов, включающих компиляцию программы, сжатие данных методом LZSS (без использования индекса или хеш-таблицы) и расчет хода в игре ним (перебором). Тесты не претендуют на полноту, но они дают некоторое представление о качестве компилятора и позволили его улучшить. Кроме того, они были выполнены почти на всех процессорах, когда-либо использовавшихся в IBM PC и могут быть интересны уже этим.

Почти во всех тестах время измерялось по таймеру машины, соответственно, погрешность измерения не превосходит 0.11 сек, как правило это меньше случайной погрешности. Каких-либо мер для устранения последней не предпринималось.

Использовались следующие компиляторы:

MIX Context.pas
CTX Context.ctx
OPT Context.opt
TP1 Turbo Pascal 1.00A
TP3 Turbo Pascal 3.02A
BP7 Borland Pascal 7.0
BC3 Borland C++ 3.1

Context.pas - первая версии context'а, написанная осенью 1994 года. Так как его входной язык очень ограничен, компилятор участвует лишь в одном тесте (и демонстрирует худший результат).

Ранние версии Turbo Pascal взяты в музее фирмы Borland, который находится на сайте community.borland.com. Наверное, наибольший интерес представляет Turbo Pascal 1.0. Третью версию я видел в 1989 году, а первую увидел лишь недавно. Это совсем маленькая программа turbo.com, включающая редактор текста, собственно компилятор и небольшую библиотеку функций. Все это как-то умещается в файле размером 33280 байт и может работать при наличии менее 64K памяти.

Тесты выполнялись на различных ПЭВМ с процессорами от 286 до Pentium 4 и Athlon XP. На этих машинах были установлены разные ОС, поэтому условия выполнения тестов не совсем одинаковы. На 286/12 установлена MS-DOS 3.30 (без smartdrv), на 386/40 - MS-DOS 6.22 (также без smartdrv), на остальных машинах - одна из систем Windows - Windows 95, Windows 98, Windows NT 4.0, Windows 2000 или Windows XP. Согласно SysInfo (Norton Utilities 8) относительное быстродействие процессора 286/12 - 9.2 (цифра странная, эта машина примерно в шесть раз быстрее IBM PC, но за неимением последней проверить это нельзя). Некоторые результаты трудно объяснить, но большая часть результатов соответствует ожиданиям.

Компиляция:

Для этого теста использовалась промежуточная версия компилятора context.tmp (99832 байта, 4628 строк). Это немного уменьшеный context.opt, сделанный только из-за имевшей место ошибки в ассемблере, который выдавал лишние префиксы замены сегмента, из-за этого код самого context.opt не умещался в 64 килобайта.

Длины исполняемых модулей context.ctx/context.cpp, байт:

CTX OPT CPP
61438 52017 56464

При компиляции программы context.cpp была задана оптимизация по скорости. При оптимизации по размеру кода получается файл размером 42048 байт и скорость его работы не ухудшается.

Время компиляции программы context.tmp, сек:

CTX OPT BC3
Celeron/1400 0.7 0.6 0.5
P4/2400/533 0.8 0.7 0.5
Celeron/1000 1.0 1.0 0.5
Celeron/2000 0.8 0.7 0.6
Athlon XP/2000+/1667 0.5 0.4 0.7
Athlon/900 1.0 0.9 0.7
Duron/600* 1.2 1.0 0.8
P4/1500 2.8 2.6 0.9
Celeron/667 3.6 3.4 1.0
Celeron/433 2.0 1.9 1.1
PII/350 1.6 1.5 1.3
MII-PR233/187 2.9 2.5 1.4
PII/233 2.4 2.2 1.7
K6/300 7.2 7.0 1.8
K6/200 3.0 2.6 1.9
P/200MMX 2.6 2.2 2.1
Celeron/266 2.6 2.3 2.8
P/133 4.0 3.4 3.0
Am486DX/66 28.7 26.8 10.2
Am386DX/40 42.5 35.1 42.2
286/12 197.6 171.1 182.0

Сравнение с Borland C++ не вполне корректно, т.к. компилятор context.cpp не эквивалентен context.tmp. Вообще, тест неудачен - в нем очень большую роль играет вывод на экран, особенно это заметно на Am486DX/66 с ISA VGA. Но с другой стороны - есть некая программа и мы не вдаваясь в особенности реализации определяем скорость ее работы на разных машинах. Результаты, отмеченные звездочкой немного занижены из-за упомянутой ошибки в ассемблере. Результат K6/300 совершенно непонятен.

Время компиляции просто огромно - для сравнения Borland Pascal 7.0 на 286/12 компилирует программу context.pas (~2400 строк текста) за пять с небольшим секунд. Конечно, на современных машинах это не заметно, но на 286 и, тем более, 8088 скорость компилятора сложно было игнорировать. На той же 286/12 Borland C++ 2.0 тратил на полную компиляцию прототипа edit64k (всего 400 строк) примерно 45 секунд.

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

можно в разы повысить скорость работы компилятора. Поиск в массиве ALPHA видимо был сделан из соображений 'переносимости'. Такая запись действительно не зависит от кодировки символов, но работает очень медленно...

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

Для следующего теста использовался построенный на иснове context.min прямой генератор кода. Длины исполняемых модулей, байт:

CTX OPT
34711 27064

Время компиляции context.min (63838 байт, 3549 строк) и context.pas (<=65001 байт, <=2395 строк - эти цифры немного различны для разных версий Turbo Pascal), сек:

CMP CTX OPT TP1 TP3 BP7
P4/2400/533 0.45 0.02 0.02 н/д н/д 0.02
Celeron/2000 0.46 0.02 0.02 н/д н/д 0.02
Athlon XP/2000+/1667 3.30 0.03 0.03 н/д н/д 0.02
Celeron/1400 0.38 0.03 0.03 н/д н/д 0.03
Celeron/1000 н/д 0.04 0.03 н/д н/д 0.03
P4/1500 1.60 0.05 0.04 н/д н/д 0.03
Athlon/900 0.64 0.06 0.04 н/д н/д 0.04
Celeron/667 1.90 0.09 0.07 н/д н/д 0.05
PII/350 1.01 0.08 0.07 н/д н/д 0.05
Celeron/433 1.20 0.10 0.08 н/д н/д 0.06
PII/233 1.49 0.14 0.11 н/д н/д 0.07
K6/300 4.08 0.12 0.10 н/д н/д 0.09
K6/200 1.75 0.12 0.09 н/д н/д 0.10
Celeron/266 2.05 0.13 0.09 н/д н/д 0.13
MII-PR233/187 1.66 0.15 0.13 н/д н/д 0.10
P/200MMX 1.67 0.18 0.13 н/д н/д 0.13
P/133 2.61 0.22 0.16 н/д н/д 0.18
Am486DX/66 15.73 0.78 0.62 0.80 н/д 0.41
Am386DX/40 26.24 2.23 1.65 2.50 1.40 1.33
286/12 119.34 9.48 7.28 9.50 5.50 5.32

В первой колонке (CMP) для сравнения приведено время работы компилятора CTX из предыдущего теста. Время выполнения тестов TP1 и TP3 измерялось c меньшей точностью, чем указано в таблице, лишний ноль приписан только для выравнивания чисел. В остальных случаях погрешность измерения времени не боле 0.01 секунды (тест выполнялся десять раз подряд, измерялось общее время). Скорость Turbo Pascal не достигнута, но результат сопоставим и разницу уже можно списать на недостаточную эффективность самого компилятора. К тому же есть основания полагать, что все версии Turbo Pascal написаны на ассемблере.

Компилятор context.pas не эквивалентен context.min (последний чуть меньше, но содержит на треть больше лексем), специально писать большие одинаковые программы на разных языках нет возможности...

Компиляция context.202 для Windows с помощью context.201 (DOS) и context.202 (Win32), сек:

201 202
P4/2400/533 0.07 0.04
Athlon XP/2000+/1667 0.08 0.04
Celeron/2000 0.08 0.05
Celeron/1400 0.09 0.07
Celeron/667 0.20 0.41
PII/350 0.32 0.25
K6/200 0.46 0.35
Celeron/266 0.43 0.37
P/133 0.83 0.69
Am486DX/66 2.68 2.42
286/12 38.11 н/д

Этот тест также более корректный, поскольку context.201 выполняет только последовательное чтение и последовательную запись файла. На верхнем уровне погрешность измерения 0.01 секунды слишком велика, выполнение теста сто раз подряд дало следующие результаты:

201 202
P4/2400/533 0.066 0.036
Celeron/2000 0.083 0.052

Сжатие файла:

Для этого теста программа arcdemo.ctx была переведена на Pascal и С++. Длины исполняемых модулей arcdemo.ctx/arcdemo.pas/arcdemo.cpp следующие, байт:

CTX OPT BP7 CPP
4560 3495 3888 8638

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

Время сжатия файла context.tmp (99832 байт) с помощью программы arcdemo, сек:

CTX OPT TP1 TP3 BP7 BC3
P4/2400/533 3.4 1.3 5.7 2.5 1.5 0.6
Celeron/2000 4.1 1.6 6.8 3.0 1.9 0.7
Athlon XP/2000+/1667 3.7 1.9 3.7 3.1 2.4 0.9
Celeron/1400 2.7 1.4 6.4 2.5 1.6 1.0
P4/1500 5.5 2.3 15.1 4.6 2.5 1.0
Celeron/1000 3.8 2.0 9.4 3.5 2.3 1.5
Athlon/900 7.2 3.5 6.7 5.5 5.1 1.8
Celeron/667 5.7 3.0 7.3 5.3 3.5 2.2
Duron/600* 9.8 4.9 н/д 7.5 6.9 2.5
Celeron/433 8.7 4.6 21.7 7.8 5.3 3.2
PII/350 10.9 5.8 26.9 9.6 6.6 4.0
Celeron/266 14.3 7.7 18.2 12.8 8.7 5.4
K6/300 17.0 8.1 14.2 12.8 9.3 5.4
PII/233 15.9 8.5 39.8 14.2 9.6 5.9
K6/200 26.5 13.5 21.3 19.3 14.1 8.1
MII-PR233/187 24.9 12.2 24.5 22.6 12.2 9.5
P/200MMX 27.2 13.5 35.7 22.9 16.5 11.3
P/133 37.1 20.6 54.4 36.1 25.5 17.8
Am486DX/66 144.0 78.5 180.9 137.7 81.4 56.5
Am386DX/40 592.6 356.6 631.1 503.1 347.2 230.9
286/12 2413.3 1528.4 2251.4 2087.5 1406.5 942.1

Трудно объяснить результаты, показанные Turbo Pascal - созданный им код плохо подходит для новых процессоров (Context'а это тоже касается). Этот тест также имеет недостаток - в цикле поиска совпадений есть лишние действия.

Расчет первого хода в игре ним:

(три ряда из девяти, восьми и семи предметов, брать можно не более трех, перебор всех возможных ходов), сек:

MIX CTX OPT TP1 TP3 BP7 BC3
Athlon XP/2000+/1667 5.2 4.4 2.7 5.9 4.0 3.0 1.4
P4/2400/533 3.7 4.1 2.5 15.7 4.9 2.6 1.5
Celeron/1400 3.9 3.8 2.7 6.0 3.8 2.7 1.8
Celeron/2000 4.4 4.9 2.9 18.8 5.9 3.1 1.8
Celeron/1000 10.3 5.3 4.1 8.1 5.2 3.8 2.4
P4/1500 6.3 7.1 4.2 32.7 8.1 4.3 2.4
Athlon/900 9.6 8.2 5.5 10.9 7.2 5.5 2.5
Celeron/667 15.4 8.0 6.0 12.1 7.7 5.7 3.7
Celeron/433 23.7 12.2 9.2 18.7 11.8 8.7 5.7
K6/300 21.2 18.9 11.0 19.6 15.5 10.5 6.9
PII/350 29.4 15.3 11.5 23.2 14.6 10.8 6.9
Celeron/266 38.6 20.1 15.1 30.3 19.2 14.2 9.2
PII/233 43.2 22.5 16.9 33.9 21.4 15.9 10.2
K6/200 49.0 28.6 19.1 29.2 23.6 16.0 10.3
P/200MMX* 30.7 29.1 18.6 47.2 26.5 17.4 11.2
MII-PR233/187 60.2 31.1 20.1 34.5 27.5 18.6 12.8
P/133 102.5 50.6 33.0 73.3 44.2 32.7 18.9
Am486DX/66 293.0 158.9 108.3 204.2 136.2 104.9 57.5
Am386DX/40 922.5 563.0 378.0 736.6 490.4 326.1 204.9
286/12 3870.9 2317.0 1655.0 2688.7 2101.7 1415.6 885.0

В первой колонке (MIX) приведено время работы программы, скомпилированой посредством context 0.5.

Тот же тест, но использовались компиляторы Context 1.4, Context 2.04 (в версии 2.02 нет поддержки целых со знаком), Context 2.07 и MS Visual C++ 4.2 (странно, но созданный им код оказался лучше, чем код MSVC 6.0, особенно это заметно на K6):

140 204 207 VC4
P4/2400/533 1.3 1.5 1.1 0.7
Celeron/2000 1.5 1.7 1.3 0.9
Athlon/900 н/д н/д 3.0 1.6
Celeron/667 5.4 5.3 4.6 2.6
PII/350 9.9 9.9 7.9 4.8
Celeron/266 13.1 12.9 10.5 6.3
K6/200 17.2 16.8 14.4 6.5
P/133 35.0 40.7 33.7 13.0
Am486DX/66 108.1 130.8 100.0 52.1

Видно, что 32-х разрядный код выполняется заметно быстрее 16-и разрядного, причем разница может быть значительна. По-видимому, лишь результат Сontext 1.4 можно прямо сопоставлять с результатом 16-и разрядного теста (Context 1.3/OPT), поскольку в них используются сходные методы генерации кода.

Подведем итоги:

Тесты не полны, поэтому сделанные из них выводы могут быть не вполне справедливы, но все же:

Для Pentium 4 очень важно выравнивание кода, более важно чем для предшествующих процессоров.

На основе полученных результатов был построен график зависимости быстродействия процессоров от времени. Использовались результаты из последних колонок (Borland C++ 3.1). Результаты привязаны к датам выпуска микропроцессоров а не машин, относительное быстродействие 286/12 принято равным шести, для получения быстродействия первого процессора каждого семейства (8086, 80286 и т.д.) результаты экстраполировались (исключение - Athlon, его быстродействие приведено к 1000 МГц):

За двадцать лет рост примерно в тысячу раз (1.4 раза в год), реальный рост несколько больше, т.к. 32-х разрядный код выполняеся бысрее 16-разрядного. Но полное представление о росте быстродействия может дать только 16-разрядный код, поскольку на старых машинах может быть выполнен только он. Выполнить тесты на 8086 к сожалению пока нет возможности.

P.S.

Это может показаться удивительным, но во время выполнения тестов были обнаружены неожиданные ошибки в MS-DOS и Turbo Pascal.

При запуске следующего командного файла

n0.com n0.exe

в MS-DOS 3.30 два раза выполняется программа n0.com (и перестановка строк ничего не меняет).

Turbo Pascal 1.0 неправильно компилирует программу Pascal-S (маленький компилятор подмножества Pascal и интерпретатор P-кода, первоначально использовался вместо context.pas). Ошибка связана с обработкой массивов строк:

var S: array [0..9] of array [0..9] of Char; T: array [0..9] of Char; begin S[0]:='0123456789'; T :='0123456789'; WriteLn('S=', S[0]); {выводится мусор} WriteLn('T=', T); {0123456789} end. Возможно, ошибка осталась незамеченной, поскольку в Turbo Pascal для работы со строками обычно используется отстутствующий в обычном Pascal тип String, а не массивы символов. В Turbo Pascal 2.0 ошибка была исправлена. Вообще надо сказать, что в Turbo Pascal ошибок совсем немного, за несколкько лет работы с ним (правда, с последними версиями) я не нашел ни одной. Но это не единственная ошибка, были и другие (источник - www.pcengines.com/tp3.htm):

Вопрос лишь в том, ошибки это или так было задумано? Да, видно программ без ошибок не бывает...

Сайт создан в системе uCoz