;состояние регистров к этому моменту ;Y = Points2D ;X = Fx3 ;Z = Fx2 ;i=j=k=0 ;Inv неизвестен ;C, Acc - неважно Compute4PointAffine proc X AffineMat Z Matrix ;вообще AfTransf, но это промежуточная матрица, её занесём сюда (пока не наступило сопровождение, эти ячейки напрочь не нужны!) ;по сути, Z = X * Y, где все операнды - матрицы. k 1 ;номер строки результата (и строки AffineMat) @@k_loop: j 2 ;номер столбца результата (и столбца Points2D) @@j_loop: i 3 ;номер столбца AffineMat и строки Points2D ZAcc RoundZero ;обнулить до 1/2 мл. разр @@i_loop: C [X+4j+i] FMA [Y+2i+k] iLOOP @@i_loop ;очередной результат готов... [Z+2j+k] Acc jLOOP @@j_loop kLOOP @@k_loop ;вот, какбе и всё... Compute4PointAffine endp
Чувствую, что здесь новая АЛУ покажет свой потенциал!
В кои-то веки, пока выполняется длительное "умножение с накоплением" (Fused Multiply-Add, FMA), мы успеем перейти в начало цикла, на последнем такте загрузить регистр C, и начать следующее. В итоге, на каждой итерации мы экономим 3 такта на прыжке и ещё 1 на "C". Всего внутренний цикл выполняется 24 раза, так что ожидаем экономии чуть менее 96 тактов, и это на общем фоне довольно ощутимо. Скажем, если одна итерация внутреннего цикла требовала 22 такта (18 на FMA, 1 на C и 3 на iLOOP, учитывая сброс конвейера), а теперь исполняется за 18, это экономия 22%. На деле чуть меньше, скоро увидим.
Как-то улучшить этот код вряд ли получится, разве что загнать строку "Z Matrix" из начала процедуры в цикл, непосредственно перед записью результата, поскольку там эта строка выполнится "нахаляву" (всё равно дожидаться АЛУ), на этом мы сэкономим аж ОДИН такт :) Ну давайте из жадности так и сделаем. Результат покажем уже на листинге.
Сначала исполним имеющийся код на старом АЛУ. Получающиеся результаты:
(мы всё это очень подробно разбирали начиная отсюда, впрочем, с тех пор поменялся знак последней строки, тоже "на будущее")
Выполняется эта процедура за 24,28 мкс, или 607 тактов. Нда, код маленький, но когда три вложенных цикла - выполняться он может прилично...
Теперь попробуем исполнить на новом АЛУ чуть подправленный код, вот его листинг:
Compute4PointAffine proc 27 CD0F X AffineMat 28 A204 k 1 ;номер строки результата (и строки AffineMat) 29 A10B @@k_loop: j 2 ;номер столбца результата (и столбца Points2D) 2A A003 @@j_loop: i 3 ;номер столбца AffineMat и строки Points2D 2B 8803 ZAcc RoundZero ;обнулить до 1/2 мл. разр 2C 8AC7 @@i_loop: C [X+4j+i] 2D 92D9 FMA [Y+2i+k] 2E A810 iLOOP @@i_loop 2F ED11 Z Matrix ;вообще AfTransf, но это промежуточная матрица, её занесём сюда (пока не наступило сопровождение, эти ячейки напрочь не нужны!) 30 EA80 [Z+2j+k] Acc 31 A912 jLOOP @@j_loop 32 AA13 kLOOP @@k_loop Compute4PointAffine endp
С первого раза всё выполнилось правильно (результаты совпали до бита) и заняло 498 тактов вместо 607, или на 18% меньше! Это даже лучше, чем я ожидал.
[Spoiler (click to open)]Похоже, на старом АЛУ мы здесь собрали все "невзгоды" сброса конвейера: когда мы прыгали в начало цикла, первой начинала "в одиночку" выполняться команда SrcAddr: [X+4j+i], занимая 2 такта. Затем выполнялась [Y+2i+k], также 2 такта, задерживая команду "С" на лишний такт, и только потом начиналось умножение с накоплением. Только когда оно целиком заканчивалось, мы выполняли iLOOP, затем один такт уходил целиком "на помойку" (не выполнялась ни SrcAddr, ни DestAddr), итого на одну итерацию получалось 24 такта, а с новой АЛУ время сократилось до 18 тактов, т.е без учёта всех прочих "накладных расходов". Похоже, считать надо количество прыжков из внутреннего цикла, их число равно 3 * 3 * 2 = 18. В каждом из них мы сэкономим по 6 тактов (24 - 18), что даёт 108 тактов. И ещё один экономится на переносе "Z Matrix" внутрь цикла, итого 109. Как в аптеке!
Следующая на очереди: процедура выделения крена из этой матрицы аффинного преобразования.