Генерация фракталов с помощью SSE/SSE2 - Метод FFFF
ОГЛАВЛЕНИЕ
Метод FFFF
FFFF v3.2.2, вышеназванный генератор набора Мандельброта, использует другой метод. FFFF имеет открытый исходный код, поэтому публикация его кода здесь полностью правомерна:
; xmm0 = zx xmm1 = zy xmm2 = tmp xmm3 = tmp xmm4 = px
; xmm5 = py xmm6 = результат xmm7 = 4.0
; eax = tmp ecx = счётчик цикла
MOVAPS xmm0,xmm4
XORPS xmm6,xmm6
MOVAPS xmm1,xmm5
mov ecx, ITER
ILOOP:
; xmm0=zx xmm1=zy
MOVAPS xmm2, xmm0
MULPS xmm2, xmm1
MULPS xmm0, xmm0
MULPS xmm1, xmm1
; xmm0=zx^2 xmm1=zy^2 xmm2=zx*zy
addps xmm2, xmm2
movaps xmm3, xmm0
addps xmm3, xmm1
; xmm0=zx^2 xmm1=zy^2 xmm2=2*zx*zy xmm3=zx^2+zy^2
cmpltps xmm3, xmm7
movmskps eax, xmm3
test eax, eax
jz EXIT
subps xmm0, xmm1
movaps xmm1, xmm2
; xmm0=zx^2-zy^2 xmm1=2*zx*zy xmm3=zx^2+zy^2
ANDPS xmm3, xmm7 ; xmm6 += (xmm3<радиус) ? 4.0 : 0.0
ADDPS xmm6, xmm3
ADDPS xmm0, xmm4
ADDPS xmm1, xmm5
dec ecx
jnz ILOOP
EXIT:
FFFF пытается сравнить x0^2 + x1^2 с 4.0, и только если сравнение не удается, находит новые значения для x0 и x1. Если сравнение возвращает истину, цикл прерывается, и нет нужды вычислять x0 и x1. Хотя такая стратегия может казаться хорошей, эксперименты показывают, что это не так. Посмотрите на длинную цепь: MOVAPS xmm3, xmm0; ADDPS xmm3, xmm1; CMPLTPS xmm3, xmm7; MOVMSKPS eax, xmm3. При выполнении этой цепи есть много возможностей использовать свободные блоки выполнения и параллельно выполнить вторую цепь. Но авторы FFFF не вставили никаких команд между командами этой цепи. Планировщик вынужден сам переупорядочивать команды. Иногда он терпит неудачу, и блоки выполнения остаются неиспользованными. Например, в коде между MULPS xmm1, xmm1 и SUBPS xmm0, xmm1 столь большое расстояние, что вторая команда может не попасть в очереди вовремя. В таком случае между командами добавляется дополнительная задержка. То же самое касается ADDPS xmm2, xmm2 и MOVAPS xmm1, xmm2: маловероятно, что они выполнятся без задержки.
Схема показывает идеальный случай вообще без задержек. Даже в таком случае на вычисление x0 требуется 20 тактовых циклов, а на вычисление x1 требуется 27 тактовых циклов. Для метода MOV-ADD цифры - 16 и 22 тактовых циклов, соответственно. Критический путь (отмеченный темно-серым цветом) включает в себя две команды MOVAPS, имеющие долгие задержки, тогда как метод MOV-ADD использует только одну MOVAPS на своем критическом пути.
Есть дополнительные задержки перед MOVAPS x1, x2 и MOVAPS x3, x0, потому что предыдущая команда выполнилась в другом блоке. ADDPS x3, x1 откладывается на один цикл из-за ограниченной производительности сумматора FP.
Во всем остальном MOV-ADD схож с FFFF. Оба метода используют SSE для вычисления значений 4 пикселей вместе, оба имеют одинаковое ограничение точности и одинаковый эффект дергания при масштабировании. Был использован схожий алгоритм, но команды в нем были расположены иначе для повышения производительности. Другие различия между FFFF и приведенной программой будут показаны позже.
В коде FFFF выше имена регистров изменены на имена, используемые в MOV-ADD (xmm0 для zx, xmm1 для zy, и так далее). Это изменение не влияет на производительность, но облегчает сравнение кода методов.