А такой GOTO/GOSUB работает быстрее? Можно использовать для ускорения основного цикла, например?
Вид для печати
А такой GOTO/GOSUB работает быстрее? Можно использовать для ускорения основного цикла, например?
Да, это быстрее, но использовать для ускорения цикла с IF не очень удобно, т.к. обычно запускают по RUN и выполнение программы начнется с 0й строки. Если добавить там проверку условия (первый запуск или нет), то это съест выигрыш. Еще можно делать самомодификацию программы, но на мой взгляд оно того не стоит.
В 2.97 помимо прошибания лбом стены за счет ускорения преобразования номеров строк в числа и собственно поиска строк добавил еще элементарное "кеширование" - запоминается последняя строка для GOTO и отдельно для GOSUB. Это дает заметный эффект не только в маленьких тестиках, но и в больших программах.
Идея с кешом замечательная. Вот бы вообще все строки хранить в заранее переваренном виде.
Известный факт, что продвинутые интерпретаторы (в т.ч. 4.51 и 5.29) и полукомпиляторы вместо номеров строк хранят адреса перехода, что конечно намного удобнее и быстрее при выполнении. Но для этого надо менять формат хранения программы, может когда-нибудь до этого дойдет. А пока приходится как в анекдоте про советские дороги и автомобили - "чего только не придумают эти русские, чтобы не строить хорошие дороги". Анонс - в новой версии среди прочего удалось добиться существенного прогресса по второму связанному с интенсивным поиском в рантайме фронту - поиск переменных, и продвинутый формат программы не спасет 4.51 и 5.29 от тотального поражения.
Не знал и не видел в векторовской документации, что 2.5 унаследовал от микрона функцию @ преобразующую dec->hex. Правда это не "настоящая" функция (как и TAB, SPC), ее результат нельзя присвоить символьной переменной, можно только напечатать через PRINT.
Еще одна скрытая возможность PRINT - поддержка Esc-последовательности
1Вh, 59h, 20+Y, 20+Х
для изменения координат курсора. Через нее реализованы CUR и AT, а можно использовать и напрямую, хотя это и неудобно.
Очередная вещь, про которую возможно знали все кроме меня - размер "точки" в LINE BS. Аргументы LINE BS задают во сколько раз увеличить символы по горизонтали и вертикали, тут вопросов нет. Но меня с детства удивляло, что "точки" (кроме случая LINE 1,1,BS) "внахлест". Оказывается сами они в 1.5 раза больше, чем расстояние между ними. Например при LINE 2,2,BS "точка" 3x3.
2.891 - версия с быстрым набором. Исправлены ошибки и недоработки, подробности в readme.
2.98 - очень быстрый интерпретатор.
Результаты тестов: RFBM, старт ANTIGRAV, пробежка в PROVERKA и взлет ракеты, пустой цикл, решето, Мандельброт.
Ошибки и недоработки тоже исправлены и есть уникальные особенности (как положительные так и отрицательные), подробности в readme.
Новые бейсики в картотеке: 2.891, 2.99x
в 2.98 заметил баг - после выхода из программы в интерактивный режим, PLAY продолжает играть.
Ramiros, спасибо, пофиксил.
Оказалось, что комодорщики, у которых тоже MS бейсик, знали про фишку с номером GOTO.
А еще они знали про "быстрый 0". Уже писал, что в 2.5 вместо 0 быстрее &, но это чисто векторовская фича, и в оригинальном 2.5 есть ограничения на места использования &. Но есть общемикрософтовский ноль - . (точка). В 2.5 точка быстрее всего, потом &, потом 0. В 2.98fix парсинг чисел очень сильно переделан, поэтому там 0 и . одинаково быстрые, а & чуть медленнее (и все эти варианты намного быстрее, чем в 2.5).
Небольшое дополнение про ориентиры для сравнения. По RFBM 2.98fix почти догнал (отстал в 5 тестах, немного обогнал в 2 и в 1 тесте ничья) BBC Basic на BBC micro (2 МГц 6502 без тормозов). Смотреть результаты BBC лучше не в wiki (там они странноватые), а в статье из Acorn User
Не сомневался, что не первым изобрел велосипед кеширования номеров строк в GOTO/GOSUB, но не знал конкретных аналогов. В продвинутых бейсиках атари начиная с basic xl это есть.
Нашел обсуждение bbc basic, где подтвердилось, что он сам по себе не особо быстрый, фишка действительно в быстром железе (bbc micro) с тестами на котором обычно сравнивают.
Нашел вот такой космос: https://oldbytes.space/@zxdunny/109342959566427298
Попробовал переписать для Вектора. Получается чуть-чуть медленней, чем в оригинале, но тоже красиво. Как бы ускорить?
На самом деле весьма красиво уже когда 130 FOR I=0TO40:FORJ=0TONКод:10 CLS:COLOR7,0,0:DIMK(32)
20 FORI=1TO15
30 R=1+SIN(PI/3+I/PI)
40 G=1+1*SIN(I/PI-PI/6)
50 B=1+1*SIN(3*PI/2+I/PI)
60 K=INT(R*3.5) + INT(G*3.5)*8 + INT(B*1.5)*64
70 N=(I*5)AND15:SCREEN0,N,K:K(N)=K:K(N+16)=K
80 NEXT
90 COLOR0:PLOT0,0:FORI=0TO15:COLORI:LINEI*8,0:NEXT
100 N=200:R=2*PI/235
110 X=0:Y=0:V=0:T=0:SZ=200:SW=256/SZ:SH=256/SZ
120 T=0.025
130 FOR I=0TON:FORJ=0TON
140 U=SIN(I+V)+SIN(R*I+X)
150 V=COS(I+V)+COS(R*I+X)
160 X=U+T
170 C=JAND15:COLORC-(C=0)
180 PLOT 128+U*63,128+V*63
190 NEXT:NEXT
200 REM T=T+0.025
300 N=0
310 FORI=1TO15:SCREEN0,I,K(I+N):NEXT
320 N=N+1:IFN=16THEN300
330 GOTO310
Еще один медленный Мандельброт (вариант побыстрее для вектора тут). Написан он явно так, чтобы притормозить интерпретаторы и дать фору компиляторам и полукомпиляторам, зато есть результаты других компов и бейсиков.
В списке перечислены компы, но понятно, что тестируются конкретные связки комп+бейсик.
Результаты вектора:
2.5 - 465.9 секунды, между спеком и +4.
2.98fix - 181.64 секунды, между БКшками.
В пузырящейся вселенной на мой взгляд фишка в анимации и векторовскому бейсику 4 фазы по силам.
Угу. Я потому и спросил. Просто мне не так очевидно, что именно вызывает неоправданные тормоза. Переписывать вселенную в сторону уменьшения читабельности не хочется. Хочется, чтобы оригинал был легко узнаваем. Но может быть что-то можно сделать, что помноженное на 40000 итераций, ускорит на чуть-чуть.
Четыре фазы можно попробовать. Правда тогда будет не цветное. Мне понравилось как цвета получились.
Это я про вариант Мандельброта, который рекламирует litwr. Но, конечно, пузырящаяся вселенная для 8-битных ретробейсиков подходит еще хуже. Понятно, что надо избавляться от математики внутри циклов и переходить на таблицы, о чем уже написал b2m. У автора SpecBAS в паке дем для его бейсика есть 3 варианта bubble_universe, возможно один из них менее требовательный. Насчет малого числа фаз - если оставаться в рамках бейсика и ослабить критерии трувекторизма, то есть 6128, у которого доступных плоскостей в 3 раза больше и можно даже сделать анимацию в цвете.
Хоть ZPU8080 расчехляй...
zpu8080 круче и код компактнее, но z88dk быстрее.
Да, наверное больше смысла все-таки будет освоить z88dk. Там и плавучка есть? Или ты думаешь про таблицы?
Там даже несколько библиотек плавучки, хотя для вектора с 8080 имеют смысл только две. И над выбором не придется особо думать, микрософтовская библиотека (фактически аналог математики в 2.5) лучше. У zpu8080 проблема с плавучкой в ненативности, она точная, но очень уж медленная. При любой реализации пузырей, даже на асме, я бы максимально постарался отабличить.
Уточненные и расширенные результаты тестирования неспешного Мандельброта. Исправил "палитру" символов (при копировании исходника в нее на каком-то этапе затесался лишний пробел). Доработал определение времени прогона.
06Ц (Emu/VV/v06x):
2.5 - 439.896 секунды, между спеком и +4
2.98fix - 176.198 секунды, между БКшками
06Ц (Emu80):
2.5 - 439.976 секунды, между спеком и +4
2.98fix - 176.218 секунды, между БКшками
Дополнительно протестировал 6128 (Emu/VV):
1.0 (пзу) - 388.618 секунды, между C64 и 800XL (на 11.5% быстрее 2.5 на 06Ц)
2.5 - 393.371 в Emu/393.391 в VV, между 800XL и dragon (на 10.5% быстрее 2.5 на 06Ц)
2.98fix - 166.394 секунды, между БКшками (на 5.5% быстрее 2.98fix на 06Ц)
Отмечу, что по разнице скорости на 06Ц и 6128 можно оценивать степень покомандной (не алгоритмической) оптимизированности под векторовское торможение. Если для 2.5 эта разница 10.5%, то для 2.98fix 5.5%, потому что в 2.98 в критичных местах MOV R,R/INR/DCR максимально заменены на альтернативные варианты. Еще можно добавить, что если оптимизировать 2.98 с использованием команд 8085, то он обойдет результат БК0011.
Чуть подробнее про влияние векторовского торможения. Интересующиеся историей вектора наверняка помнят фрагмент из описания прототипа: "Эффективная тактовая частота - 2.4 МГц"
Т.е. на какой частоте должен был бы работать 8080 без торможения, чтобы соответствовать вектору на 3 МГц. Посмотрим, какой "эффективной тактовой частоте" соответствуют бейсики при выполнении Мандельброта. Результаты с торможением приведены в предыдущем посте, а результаты без торможения позволяет получить Emu, если в конфиге закомментировать adjust=4
06Ц без торможения (Emu)
2.5 - 327.855 секунд
2.98fix - 143.85 секунд
Сравнивая с "тормозными" результатами получаем, что для 2.5 эффективная частота=2.2359 МГц, для 2.98fix=2.4492 МГц.
2.5 можно отнести к плохо оптизированным под вектор программам, коэффициент торможения 0.7453 просто неприличный.
Что касается 2.98fix, то в совокупности с еще одной программой - расчет знаков Пи по алгоритму spigot считаю, что "эффективная частота" 2.43-2.45+ (коэффициент торможения 0.81-0.82) для вектора - это показатель хорошей оптимизации.
Возвращясь к авторской оценке считаю ее адекватной для умеренно оптимизированных для вектора программ. И у меня самого даже есть пример такой программы - "старый" Мандельброт на асме, в котором что-то оптимизировано, а что-то как написано первый раз так и осталось. И для того Мандельброта (не помню, выкладывал ли на форум, но точно посылал svofski) получался коэффициент торможения практически ровно 0.8, т.е. "эффективная частота" 2.4 МГц.
В завершение для полноты картины еще BASCOM. Тут менее точно, т.к. засекал по секундомеру.
06Ц (Emu, mdos34) - 98 секунд, между ABC 802 и BBC Master (mode 7), т.е. вышел на второе место в том списке
06Ц без торможения (Emu, mdos34) - 74 секунды
"Эффективная частота" в данном случае=2.27 МГц, коэффициент торможения 0.76.
Ну и немного спорта:
6128 (Emu, ОС6128) - 87 секунд, первое место перед ABC 802.
Перевел программу (и пофиксил некоторые баги)из журнала Радиолюбитель 8-1991:
Цитата:
Посылаю программу для настройки телевизоров, написанную для ПЭВМ "Вектор". Коротко о себе: ученик 11-го класса средней школы N244 г.Киева ОНИЩЕНКО Игорь.
Простой (даже слишком простой) тестик, зато понятно и видно, что тестировали и как тестировали.
Время выполнения в секундах:
Amstrad CPC - 27
VIC-20 - 34
C64 - 40/38
TI-99/4A - 77
ZX Spectrum - 87
Siclair QL - 39
Atari 800XL - 50
Apple II - 36
06Ц (2.5) - 45
06Ц (2.98fix) - 21
Убрал из списка ZX80, т.к. там программа изменена и так некорректно сравнивать.
2.98 можно сказать вне конкурса, т.к. для популярных ретрокомпов есть более быстрые бейсики, которых не было в тесте.
Медленному Мандельброту (1, 2, 3, 4) понадобился эпилог.
После простенького тестика решил еще раз взглянуть на результаты Мандельброта и глаз зацепился за удивительно медленный Корвет. Решил сам проверить и получилось вот что:
Корвет, Бейсик 2.0/пзу (Emu80) - 493 секунды (по секундомеру)
По сравнению с результатом, который привел litwr (564.92 секунды) разница очень большая. Насколько помню процессор корвета не тормозится при обращении к пзу и основному озу, а один такт ожидания добавляется при обращении к (некоторым?) портам. Даже если emu80 не 100% точен такая погрешность не могла набежать.
Какое это имеет отношение к вектору - неспешного Мандельброта протестировал только из-за наличия результатов для нескольких компьютеров. Но получается, что тем результатам нельзя доверять, печальная история.
- - - Добавлено - - -
Кажется понял основную причину такой разницы. Часть интерпретаторов (а компиляторам это без разницы) при вводе удаляет лишние пробелы в начале строки, а часть не удаляет. Векторовский 2.5 удаляет, а корветовские 1.1 и 2.0 - не удаляют. В оригинале щедро насыпали пробелов в начале строк внутри циклов, на самом напряженном участке, вероятно чтобы максимально затормозить незадачливые интерпретаторы. litwr не удалял эти пробелы из корветовской версии, я удалял. Ну а векторовские 2.5-2.98 сами удаляют эти пробелы, как уже написал, поэтому заметно опередили корвет, хотя проц корвета чуть быстрее.
ivagor, используя Basic2_98fix попробовал порисовать в GRAF V5.3, есть большие проблемы с GET/PUT и PAINT, артифачит и даже вылетает в бейсик во время заливки. В 2.5 проблем нет.
Ramiros, спасибо за багрепорт, но вряд ли я смогу поправить в данном случае.
В get/put проблема понятна, но места для исправления нет. Скорее всего придется ограничится добавлением в readme описания ограничения. Ограничение совместимости в данном случае это плата за скорость и компактность, увы.
А для paint хотелось бы увидеть конкретный пример зависания или вылета, у меня пока не получилось воспроизвести.
Для graf 5.3 (и других подобных программ, которых я пока не знаю) могу порекомендовать более консервативный 2.891.
- - - Добавлено - - -
Наверно стоит написать, в чем проблема с put в graf 5.3. Там две картинки для последующих put получают не через get, а с помощью poke в массив. А внутренний формат хранения картинок get/put в 2.98 другой, иначе невозможно было сделать быстрый байтово-пиксельный get/put. Для совместимости в graf 5.3 пришлось бы добавить в poke детект того, что пишем в область памяти определенную как массив, предположить, что это для будущего put и преобразовывать на ходу записываемые значения. И это решение тоже было бы не универсальным, т.к. можно написать программу, которая будет делать poke в область массива, но не для put. Повторюсь, признаю, что 2.98 несовместим с graf 5.3, используйте 2.891.
Понятно, но тут скорее проблема не в самом paint, а опять в get/put. В 2.891 все нормально.
Ну и на солнце тоже есть пятна, в оригинальном paint были две ошибки, которые исправил в 2.80 и 2.88. Другое дело, что авторы "классических" программ если напарывались на те ошибки, то могли их обходить.
1. В 2.99 исправил ошибку GET (была в 2.90-2.98), которая в GRAF при определенных условиях приводила к вылету в бейсик при последующем PAINT.
2. Сделал универсальный вариант GRAF 5.3U, который работает и в 2.5-2.891 и в 2.99. Программа при этом не увеличилась, а даже немного сократилась. Можно резюмировать, что без POKE внутрь массива GET вполне можно обойтись.
Ramiros, еще раз спасибо за багрепорты!
Upd 07.12.2023: 2.99fix - исправлен RENUM
Upd 26.12.2023:
В старых MS бейсиках на очень многих платформах есть баг (на 8080 его исправили в 5.x), проявляющийся при ошибке в вычислении функции VAL (видео). В 2.991 исправил, но пришлось убрать восьмеричные числа, которые были с 2.61 (в 2.5 их не было), т.к. места не хватало. 2.99fix не убираю на случай если кому-то хочется восьмеричных и не так важны ошибки при вычислении VAL.
Upd 27.02.2024: 2.992 - Исправлены/доработаны CLS и определение переполнения порядка числа при его переводе из символьного представления в двоичное.
Upd 09.03.2024: 2.993 - Исправлен RETURN, небольшие опимизации.
2.99x в картотеке
Решил узнать, сколько быстродействия у медленного Мандельброта крадет зловредное оформление программы. Убрал REM и лишние пробелы, максимально "упаковал" в строки через двоеточние, перенумеровал с минимальным шагом. Алгоритм и все операции остались без изменений.
06Ц 2.5 (Emu) - 403.854 секунды вместо 439.896 у оригинала, на 8% быстрее
06Ц 2.99 (Emu) - 166.913 секунды вместо 176.198 у оригинала, на 5% быстрее
2.99 эффективнее борется с избыточностью программы.
Корвет бейсик 2.0/пзу (Emu80) - 486 секунд (по секундомеру) вместо 564.92 у оригинала (по данным litwr, Emu), на 14% быстрее
Повторюсь, что корветовский бейсик сам не удаляет начальные пробелы в строке, поэтому эффект от уборки мусора больше. У корвета еще остается резерв в виде частичного использования целых.
На мой взгляд тот тест в оригинальном виде проверяет скорее не быстродействие, а способность бейсика бороться с искусственными препятствиями, т.к. сложно представить например автора программы для корвета, который оптимизируя по скорости оставит кучу пробелов на критичном участке.
Все таки ты оцениваешь этот бенчмарк очень предвзято, имея глубокие познания о том, как работают Бейсики. Мы от тебя узнали, что Корвет тормозит от пробелов и теперь трудно это забыть обратно. Но мне как раз очень легко представить себе, что кто-то писал для Корвета, используя пробелы. Особенно потому, что скорее всего эту фичу кто-то сделал нарочно, чтобы придать Бейсику какое-то подобие структурности.
Ну и смысл бенчмарка по-моему не в запредельной оптимизации, а в сравнении разных величин при фиксированной постоянной.
1. Работа над ошибками.
Сначала сделал почти правильно. Потом, к сожалению, убрал один символов из "палитры", отсутствующий в 2.5 (и не только в 2.5 и не только на векторе). Это немного влияет на результат, что особенно важно при сравнении с другими компьютерами, поэтому вернул символ в "палитру" (вместо ~ использовал -). Время везде в секундах.
Оригинал:
06Ц 2.5 (Emu) - 453.375
06Ц 2.99 (Emu) - 181.43
Корвет 2.0/пзу (Emu, по данным litwr) - 564.92
Оптимизированный вариант:
06Ц 2.5 (Emu) - 416.214, на 8% быстрее оригинала
06Ц 2.99 (Emu) - 171.905, на 5% быстрее оригинала
Корвет 2.0/пзу (Emu80, по секундомеру) - 501 секунда, на 11% быстрее оригинала
Чтобы не было сомнений приложил исходники.
2. На мой взгляд предвзятость - это введение в тест быстродействия нескольких элементов, которые часть бейсиков проигнорируют, а часть будут тратить на них время.
От пробелов тормозят все интерпретаторы, но нюанс в том, что некоторые (потомки msbasic 3.2, в т.ч. векторовский 2.5) убирают часть незначащих пробелов при вводе строки, а часть (более поздние msbasic, в т.ч. корветовский и, например, msxный) не убирают.
Особое оформление исходника - это нормально например в целях обучения. Но если это тест быстродействия и элементы оформления влияют на скорость, то по моему мнению их (элементы оформления) надо минимизировать.
До каких пределов минимизировать - вопрос для обсуждения. Как вариант - минимизировать до достижения максимальной переносимости теста при сохранении его правильной работы.
При таком подходе точно лишние:
1. Куча строк с комментариями, которые часть бейсиков будут пробегать каждый раз в цикле при поиске номера строки, а часть проигнорируют.
2. Пробелы в начале строк.
3. Длинные номера строк.
Пробелы между операторами части бейсиков (но не 2.5) нужны, тут компромисс более-менее понятен.
Разбиение всех операций на индивидуальные строки, без двоеточий - понятно, кому это нужно, но для MSBASICов это тормоз.
Также отмечу, что в подобных тестах целесообразно приводить точность вычислений для каждой платформы. У 2.5 и Корвета (в данном тесте!) 3 байта мантиссы и 1 байт экспоненты, двоичное представление.
Результаты лучше смотреть не в посте litwr, а на gitlab, т.к. там приведена точность расчетов и на чем тестировали.
Бесконечно можно не только смотреть на огонь, воду и работу других людей, но и обсуждать Мандельброта. На неделе придумал, как еще оптимизировать поиск двухбуквенных переменных (оригинальный Мандельброт за 163.618 секунды, на 10% быстрее 2.99), что позволяет обойти в таблице результатов БК0011. Но резервов по свободному месту уже не было, пришлось пойти на пару небольших компромиссов. Проблема в том, что пока не нашел других программ, в которых эта оптимизация давала бы настолько выраженный эффект, поэтому оставил этот бронепоезд на запасном пути. Тогда решил зайти с другой стороны - раз в других программах новая оптимизация мало что дает, значит основная проблема не в бейсике, а в данной реализации Мандельброта.
Показываю фокус, следите за руками. Берем manlt_optimized_corrected_06С.cas (который с косметическими оптимизациями) и всего лишь добавляем в первую строку нициализацию наиболее используемых переменных - получили manlt_optimized2_06С.cas. И вуаля - он выполняется в 2.99 за 144.509 секунды, чуть опережая даже BBC Basic на BBC Micro, и с запасом обходя БК0011 и Amstrad CPC. Подводя итог можно сказать, что удалось разоблачить и обезвредить диверсионную реализацию Мандельброта. Не стоит воспринимать этот тест слишком серьезно, хотя можно взять на заметку некоторые моменты.
Если важна скорость в векторовских родственниках 2.5 (и в многочисленных родственниках msbasic 3.2 на других компьютерах), то:
1. Минимизируйте число пробелов.
2. Перенумеруйте финальную версию с шагом 1, чтобы получить минимальную длину символьного представления номеров строк.
3. В финальной версии минимизируйте число комментариев.
4. "Упаковывайте" операции в строки через двоеточия.
5. Если переменных много, и некоторые из них используются намного чаще других, то стоит их инициализировать в первую очередь. Для 2.98-2.99 это не касается числовых переменных с однобуквенными именами, они в любом случае будут обрабатываться с максимальной скоростью.
Пункты 1-3 можно доверить роботу.
Пункты 1-4 кроме ускорения одновременно приводят и к сокращению размера программы.
Результаты тестов:
2.5 - 313.279 секунды
2.891 - 257.927 секунды
2.995 - 144.209 секунды
2.996 - 133.606 секунды
Получилось нечто похожее на красивую трассировку лучей. Списал домашку у Paul Dunn. 5 цветов (в т.ч. "полутоновые" тени), четкие границы квадратов в отражении благодаря точной плавучке. Красота требует жертв, готовьте супервекторы или запасайтесь терпением. Время рисования в 2.5 - примерно 389 минут (6 часов 29 минут), в 2.99 - примерно 165 минут (2 часа 45 минут).
Выглядит феноменально. Немного медленно, но ничего, ведь можно построить рендерфарм из тысяч Векторов и рендерить блокбастеры.
Интересно, насколько реально сделать небо неоднородным, чтобы у сфер макушка как-то выделялась?
Эта картинка уже не "как на спеке, только медленнее", а все же векторовская, мне нравится. Из компов с 8080 еще на корвете можно сделать примерно так, только там нет чего-то вроде 2.99, чтобы побыстрее. Полутоновые макушки сфер - это следующее, что хотелось бы, осталось найти, у кого списать пример попроще. Параллельно можно думать про конверсию в асм с прицелом на многопроцессорные супервекторы.