Цитата:
Для повышения скорости компиляции кода нужно исключить поиск имен в словарях во время компиляции.
...
А это и есть сolorForth'овские preparsed sourсes, о которых вам говорят. Или другое название -- цикл ICE (interpretation-compilation-editing). То есть перенос некоторой части работы компилятора и на редактор тоже. Непривычно и по-стандарто-борчески, но крайне эффективно и показало жизнеспособность в оригинальной системе Мура.
Недавно чё-то
вякал подобное по этому поводу в
чате:
profiT1984 писал(а):
А вообще, я тут думаю, как разумный компромисс между скоростью генерации-компиляции-оптимизации и скоростью выполнения можно временно переходить в шитый код... И не просто в подпрограммный, отключением оптимизатора, а допустим, косвенный...
Так как выясняется что по скорости критичны оказаться могут обе фазы..
Или как другой но уже несколько оторванный от реальности вариант: использовать в генерации не EVALUATE , а что-то другое.. Допустим диалект colorForth. Он парсится, компилируется, и даже оптимизируется гораздо легче...
Вариант с colorForth'ом в принципе предпочтительнее, но это писать достаточно много, пусть даже и есть две работающие намётки:
~profit/lib/colorSP4th.f и
~profit/lib/loveall.f.
А теперь, вести с полей динамической генерации кода:
Есть вот такой лозунг: "No stinking loops". Лозунг хороший, и даже более того -- полезный. Первым на гильотину у меня взойдут циклы со счётчиком, то есть DO LOOP (и FOR NEXT , за компанию). Говорить что последовательный пробег от одного числового значения к другому значению используется часто -- ничего не сказать. Это и проход по ячейкам памяти, это и 1, 2, 3, 4, 5 , это по сути если и не альфа и омега, то пол-греческого алфавита -- точно. А меж тем вы-факторизи-ро-вы-ваются (э-э-э.. ну, вы поняли) циклы только bac4th-итераторами.
~profit/lib/bac4th-iterators.f -- вот там есть слово iterateBy . Вещь, казалось, бы незамысловатая, элементарная и низкоуровневая. Слово это берёт со стека начальное число, длину, и шаг и последовательно проходит всю длину от начала делая по шагу в каждой итерации:
Код:
REQUIRE iterateBy ~profit/lib/bac4th-iterators.f
\ Пример работы слова iterateBy ( start len step --> i \ i <-- i )
: r 1 10 1 iterateBy DUP . ;
\ вывод: 1 2 3 4 5 6 7 8 9 10
r
Вообще, для такой постоянно используемой элементарщины как 1, 2, 3, 4, 5 ; bac4th -- слегка жирно и неэкономно, хотя и очень концептуально и гибко. Чтобы ускорить это дело можно заиспользовать генерацию кода. Генерируем (в ходе исполнения, само собой) примерно такой кусочек кода (start, step и end вкомпилировываются в код во время генерации):
Код:
PRO
start
BEGIN
( i ) CONT ( i )
step +
DUP end > UNTIL
DROP
И пускаем его пастись EXECUTE'ом с передачей успехов этого сгенерированного итератора дальше. В результате экономим как память в виде ячеек R-стека, так и и процессорное время.
В качестве ещё несколько более хакового, но гораздо более быстрого варианта, можно использовать тот факт что этот сгенерированный цикл-итератор используется только один раз, и, соответственно, адрес для вызова успеха будет только один и не изменится:
Код:
PRO
start
BEGIN
( i ) [ success-xt COMPILE, ] ( i )
step +
DUP end > UNTIL
DROP
Тогда будет экономия и L-стека используемого в PRO ... CONT .
Несмотря на то что, по идее, эти циклы должны рвать всё и вся по скорости, на деле, в некоторых случаях, они оказываются даже медленнее своих статических братьев -- негенерируемых DO LOOP и BEGIN UNTIL. Почему? Тормоза вылезают в достаточно нетривиальной и слегка тяжёлой (каламбур, да) генерации кода которая основана на SPF-ном EVALUATE. Там тормозов даже несколько: разбор, поиск, оптимизатор. Не надо говорить, что там в принципе всё достаточно быстро: если попробовать погенерировать по паре миллионов замыканий, и пройтись profiler'ом, то это всё вылезет на поверхность. Ситуация существенно улучшается при включении хэш-поиска по словарям (
~pinka/spf/quick-swl.f), и отключения оптимизатора (это не шутка), хотя бы на время генерации кода. Тем не менее, я пока так делать не хочу...
Как приемлемое решение в работу, рассматриваю выбор одного из нескольких вариантов iterateBy в зависимости от намечаемой продолжительности цикла, то есть если цикл коротенький, и генерация кода цикла-итератора займёт больше времени чем сама обработка цикла, то будет выбираться статический вариант, если цикл масштабен и выше только небо то генерация цикла-итератора становится копеечными вложениями, приносящими при обработке горы сэкономленного процессорного времени. Решение, очевидно принимается в ходе исполнения, в зависимости от полученных данных:
Код:
: iterateBy ( start len step --> i \ i <-- i ) PRO
2DUP 6 LSHIFT ( 2* 2* 2* 2* 2* 2* ) <
\ Решаем: если кол-во итераций в цикле будет меньше чем, скажем 64 (взято с потолка),
IF iterateBy2 CONT EXIT THEN
\ то циклуем статически,
iterateBy4 CONT ;
\ иначе, если больше чем 64, -- то генерируем цикл и пускаем в нём
При этом эти внутренние пертурбации одного словечка iterateBy (порядка пяти-шести вариантов) внешне остаются незаметными для программ, так как все вариации iterateBy делают одно и то же -- проход по числовому ряду.
Coming soon on CVS...