Данная статья является продолжением предыдущей статьи.
Здесь рассматривается вариант прямой векторизации алгоритма FFT.
Перед чтением рекомендуется бегло ознакомиться с предыдущей статьёй.
Промежуточные результаты представлены на графиках в конце каждого раздела реализации.
Итоговые результаты показаны на графиках в разделе Собираем FFT
Исходный код
Исходный код лежит здесь.
Структура кода намеренно сделана как можно проще.
Приоритеты в порядке убывания:
— Скорость выполнения
— Единообразие
— Понятность
Компиляция:
./compile.sh */*.c
Проверка корректности:
./check_all.sh 12 1000 x
Измерение скорости:
taskset 1 ./speed_all_stage.sh 12 10000 x
taskset 1 ./speed_all_fft.sh 12 10000 x
taskset 1 нужен для корректного чтения счётчика тактов (привязка к первому ядру).
Опции компиляции
При компиляции использовались следующие опции lcc:
-Wall -O3 -faligned -ffast-math -march=elbrus-v5
Почему я компилирую для elbrus-v5 написано здесь.
Как измерялось время
Замеры времени делались с помощью функции clock_gettime():
struct timespec t0, t1; clock_gettime(CLOCK_REALTIME, &t0); /*** здесь измеряемый код ***/ clock_gettime(CLOCK_REALTIME, &t1); int usec = (t1.tv_sec - t0.tv_sec)*1000000 + (t1.tv_nsec - t0.tv_nsec)/1000;
Также использовалось чтение счётчика тактов процессора:
uint64_t get_clock_count() { uint64_t dst; #pragma asm_inline asm ("rrd %%clkr, %0" : "=r" (dst)); return dst; } ... uint64_t ticks0 = get_clock_count(); /*** здесь измеряемый код ***/ uint64_t ticks1 = get_clock_count(); uint64_t ticks = ticks1 - ticks0;
Как создавались графики
Графики рисовались в Google Sheets.
Данные для графиков собирались с помощью скрипта data_for_diagram.py, который запускает программу измерения скорости с разными параметрами и парсит её вывод.
Пример запуска скрипта:
taskset 1 ./data_for_diagram.py \
./fft_radix2/fft_radix2_speedtest 4 21 1 1000 -1 > out.txt
taskset 1 нужен для корректного чтения счётчика тактов (привязка к первому ядру).
Содержание:
Пишем векторный Reverse
reverse_radix2_vector2
reverse_radix2_vector4
reverse_radix4_vector2
Пишем векторный Stage
stage_radix2_vector2
stage_radix2_vector4
stage_radix4_vector2
Пишем LastStage
lastStage_radix2
Что такое прямая векторизация
Для ряда операций (сложение, умножение и так далее) в процессоре Эльбрус присутствуют векторные инструкции (SIMD). Векторные инструкции выполняют несколько одинаковых операций одновременно.
В предыдущей статье векторные инструкции использовались просто как набор из нескольких инструкций в одной. Например, вместо двух инструкций умножения float чисел, нужных по алгоритму, применялась одна векторная, выполняющая два умножения (SIMD64).
Проблема такого подхода в том, что нет простого способа ускорить алгоритм через замену инструкции SIMD64 на аналогичную SIMD128. Использование SIMD128 приводило к появлению инструкций перетасовки данных (shuf, perm), занимавших исполнительные юниты.
В этой статье векторные инструкции используются по‑другому.
Здесь с их помощью создаются «векторные» версии алгоритмов из исходных.
Векторные алгоритмы выполняют несколько одинаковых задач одновременно аналогично тому, как векторные инструкции выполняют несколько одинаковых операций одновременно.
Такой подход я и называю «прямой векторизацией».
Для выполнения прямой векторизации нужно заменить обычные инструкции на их векторные версии. Чтобы такое было возможно, в исходной программе должны использоваться инструкции, для которых существуют векторные версии. Обрабатываемые данные также требуется привести в векторную форму, то есть такой формат, который ожидается векторными инструкциями.
Прямая векторизация кода на Си
Пусть, например, алгоритм обрабатывает float числа.
Для выполнения прямой векторизации в 2 или 4 раза нужно заменить в Си‑коде исходного алгоритма тип float на __v2sf или __v4sf соответственно. И компилятор сгенерирует векторизованный код с использованием инструкций SIMD64 или SIMD128 соответственно.
При этом операции «+», «-», «*» будут скомпилированы в векторные версии инструкций.
Обрабатываемые данные также требуется предварительно привести в векторную форму:
там, где было одно
floatчисло, теперь должен быть «вектор»floatчисел (расположенные рядом 2 или 4floatчисла)там, где был массив
floatчисел, теперь должен быть массив «векторов»floatчисел (фактически исходный массив преобразуется в набор перемежённых массивов)
Прямая векторизация алгоритма Stage
Пусть изначально есть следующий код:
void stage_radix2_etalon(int data_count, myComplex *data_in, myComplex *data_out, myComplex *coef) { myComplex *x_in = &data_in[0]; myComplex *y_in = &data_in[1]; myComplex *c_in = coef; myComplex *out_add = &data_out[0]; myComplex *out_sub = &data_out[data_count/2]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < data_count/2; ++i) { myComplex x = x_in[2*i]; myComplex y = y_in[2*i]; myComplex c = c_in[i]; myComplex cy = complex_mul(c, y); out_add[i] = complex_add(x, cy); out_sub[i] = complex_sub(x, cy); } }
Избавимся от типа myComplex, оставив просто тип float:
void stage_radix2_etalon(int data_count, float *data_in, float *data_out, float *coef) { float *x_real_in = &data_in[0]; float *x_imag_in = &data_in[1]; float *y_real_in = &data_in[2]; float *y_imag_in = &data_in[3]; float *c_real_in = &coef[0]; float *c_imag_in = &coef[1]; float *out_add_real = &data_out[0]; float *out_add_imag = &data_out[1]; float *out_sub_real = &data_out[data_count/2 + 0]; float *out_sub_imag = &data_out[data_count/2 + 1]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < data_count/2; ++i) { float x_real = x_real_in[4*i]; float x_imag = x_imag_in[4*i]; float y_real = y_real_in[4*i]; float y_imag = y_imag_in[4*i]; float c_real = c_real_in[2*i]; float c_imag = c_imag_in[2*i]; float cy_real = c_real*y_real - c_imag*y_imag; float cy_imag = c_real*y_imag + c_imag*y_real; out_add_real[2*i] = x_real + cy_real; out_add_imag[2*i] = x_imag + cy_imag; out_sub_real[2*i] = x_real - cy_real; out_sub_imag[2*i] = x_imag - cy_imag; } }
Получившийся результат будем называть «базовым алгоритмом Stage» для прямой векторизации.
Если в этом коде заменить все float на __v2sf, получим прямую векторизацию в 2 раза.
Если в этом коде заменить все float на __v4sf, получим прямую векторизацию в 4 раза.
К сожалению, я узнал про этот приём уже после того, как написал весь код.
Поэтому сейчас в коде используются интринсики вместе с типами uint64_t и __v2di там, где в базовом алгоритме стоят операции «+», «-», «*».
В будущем я, скорее всего, исправлю код.
Прямая векторизация полного алгоритма FFT
Алгоритм FFT состоит из одного запуска Reverse и нескольких запусков Stage.
Stage был векторизован в предыдущем разделе.
Reverse векторизуется аналогичным образом.
Объединив векторные Reverse и Stage, получим векторный алгоритм FFT.
Этот алгоритм ожидает на входе и выходе набор перемежённых массивов float чисел.
В результате его работы будут вычислены FFT от каждого из перемежённых массивов.
Решение задачи FFT с помощью векторного FFT
Итак, мы умеем вычислять несколько FFT одновременно.
Как использовать это умение, чтобы вычислить FFT от одного набора комплексных чисел?
Для этого нужно вспомнить, как выглядел рекурсивный алгоритм Кули‑Тьюки:
Вычисляются FFT от каждого из перемежённых массивов комплексных чисел.
Вычисляется итоговый FFT с помощью этих перемежённых FFT.
Первый шаг сделаем с помощью векторного алгоритма FFT.
Векторный FFT ожидает на входе и выходе набор перемежённых массивов float чисел, а алгоритм Кули‑Тьюки ожидает набор перемежённых массивов комплексных чисел.
Поэтому добавим перед этим шагом преобразование исходных данных в векторную форму, а после этого шага — преобразование из векторной формы в исходную.
Схемы преобразования данных в векторную форму


Теперь на вход можно подавать массив комплексных чисел.
Алгоритм будет воспринимать его, как набор перемежённых массивов комплексных чисел.
В результате его работы будут вычислены FFT от каждого из перемежённых массивов.
Второй шаг похож на то, что делает адаптированный для Эльбруса алгоритм Stage из прошлой статьи. Если точнее, на этом шаге выполняются те же действия, что делает самый последний Stage в алгоритме FFT (тот Stage, в котором используется полный набор разных коэффициентов). Поэтому далее второй шаг будем называть «LastStage».
В случае выполнения прямой векторизации в 2 раза (когда получаются 2 перемежённых массива) в качестве LastStage можно использовать любой stage_radix2 из прошлой статьи.
В случае выполнения прямой векторизации в 4 раза (когда получаются 4 перемежённых массива) в качестве LastStage можно использовать любой stage_radix4 из прошлой статьи (или сначала stage_radix2_vector2 и затем любой stage_radix2 из прошлой статьи).
Итого, полный порядок действий выглядит так:
Исходные данные преобразуются в векторную форму.
Выполняется векторный Reverse.
Несколько раз выполняется векторный Stage.
Полученные данные преобразуются из векторной формы в исходную.
Выполняется LastStage.
Преобразования данных в векторную форму и обратно — достаточно простые процедуры. Поэтому в итоговой реализации они сразу включены внутрь Reverse и LastStage соответственно.
Пишем векторный Reverse
reverse_radix2_vector2
1. reverse_radix2_vector2_etalon
Эталонный вариант для сравнения на корректность.
Здесь reverseNumber_radix2 вычисляется с помощью цикла.
int reverseNumber_radix2(int number, int bit_count) { int answer = 0; for(int i = 0; i < bit_count; ++i) { answer <<= 1; answer |= number & 1; number >>= 1; } return answer; } void reverse_radix2_vector2_etalon(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; for(int64_t i = 0; i < count_out; ++i) { int index = reverseNumber_radix2(i, bit_count_out); myComplex a = data_in[2*i + 0]; myComplex b = data_in[2*i + 1]; data_out[index] = (myComplex2){.real = {a.real, b.real}, .imag = {a.imag, b.imag}}; } }
2. reverse_radix2_vector2
В процессоре есть инструкция bitrevd, которая производит операцию reverseNumber_radix2 над 64-битным числом. Заменим reverseNumber_radix2() на __builtin_e2k_bitrevd().
Код на Си
void reverse_radix2_vector2(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in = (__v2di*)data_in; __v2di *out = (__v2di*)data_out; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out; ++i) { int64_t index = __builtin_e2k_bitrevd(i) >> shift_out; __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; out[index] = __builtin_e2k_qppermb(in[i], in[i], mask); } }
Основной цикл на ассемблере
.L4793: { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=1, asz=5, abs=0, disp=0 } .L4668: { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END bitrevd,0,sm %b[11], %b[13] qppermb,1,sm %b[10], %b[10], %r4, %b[12] addd,2,sm %b[11], 0x1, %b[9] shrd,3,sm %b[17], %r0, %b[19] shld,4,sm %b[21], 0x4, %b[20] stqp,5 %r2, %b[22], %b[18] movaqp,1 area=0, ind=0, am=1, be=0, %b[0] }
Теоретическая скорость: 2 комплексных числа за 1 такт (2/1) = 16 Байт/такт
Замеры скорости

3. reverse_radix2_vector2_x2
Сделаем ручную раскрутку цикла в 2 раза.
Сразу напишем код так, чтобы вместиться в один такт (убираем инструкции shrd и shld).
Код на Си
void reverse_radix2_vector2_x2(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; int64_t delta = (1LL << shift_out) / 16; __v2di *in = (__v2di*) data_in; __v2di *out_0 = (__v2di*)&data_out[0]; __v2di *out_1 = (__v2di*)&data_out[count_out/2]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0, shifted_i = 0; i < count_out; i += 2, shifted_i += 2*delta) { int64_t offset = __builtin_e2k_bitrevd(shifted_i); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)out_0 + offset) = __builtin_e2k_qppermb(in[i + 0], in[i + 0], mask); *(__v2du*)((void*)out_1 + offset) = __builtin_e2k_qppermb(in[i + 1], in[i + 1], mask); } }
Основной цикл на ассемблере
.L5010: { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 } .L4823: { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END bitrevd,0,sm %b[22], %b[13] qppermb,1,sm %b[10], %b[10], %r5, %b[19] stqp,2 %r4, %b[17], %b[21] qppermb,3,sm %b[11], %b[11], %r5, %b[23] addd,4,sm %b[20], %r0, %b[18] stqp,5 %r2, %b[17], %b[25] movaqp,0 area=0, ind=16, am=1, be=0, %b[0] movaqp,1 area=0, ind=0, am=0, be=0, %b[1] }
Теоретическая скорость: 4 комплексных числа за 1 такт (4/1) = 32 Байт/такт
Замеры скорости

Видим ускорение в начале графика.
Здесь происходит два чтения из одного места памяти и запись в два разных места памяти.
4. reverse_radix2_vector2_stream2
Теперь сделаем наоборот: будем читать из двух разных мест, а писать рядом.
Чтение в 2 потока из разных мест памяти, запись в одно место памяти.
Сразу напишем код так, чтобы вместиться в один такт (убираем инструкции shrd и shld).
Код на Си
void reverse_radix2_vector2_stream2(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; int64_t delta = (1LL << shift_out) / 16; __v2di *in_0 = (__v2di*)&data_in[0]; __v2di *in_1 = (__v2di*)&data_in[count_in/2]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0, shifted_i = 0; i < count_out/2; ++i, shifted_i += delta) { int64_t offset = __builtin_e2k_bitrevd(shifted_i); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_0[i], in_0[i], mask); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_1[i], in_1[i], mask); } }
Основной цикл на ассемблере
.L5212: { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=2, asz=5, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=1, asz=5, abs=0, disp=0 } .L5039: { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END bitrevd,0,sm %b[22], %b[13] qppermb,1,sm %b[11], %b[11], %r5, %b[23] stqp,2 %r2, %b[17], %b[25] qppermb,3,sm %b[10], %b[10], %r5, %b[19] addd,4,sm %b[20], %r4, %b[18] stqp,5 %r0, %b[17], %b[21] movaqp,1 area=0, ind=0, am=1, be=0, %b[1] movaqp,3 area=0, ind=0, am=1, be=0, %b[0] }
Теоретическая скорость: 4 комплексных числа за 1 такт (4/1) = 32 Байт/такт
Замеры скорости

Видим желаемое ускорение по всей длине графика.
5. reverse_radix2_vector2_stream4
Чтение в 4 потока из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix2_vector2_stream4(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_00 = (__v2di*)&data_in[0 * count_in/4]; __v2di *in_01 = (__v2di*)&data_in[1 * count_in/4]; __v2di *in_10 = (__v2di*)&data_in[2 * count_in/4]; __v2di *in_11 = (__v2di*)&data_in[3 * count_in/4]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/4; ++i) { int64_t offset = 16 * (__builtin_e2k_bitrevd(i) >> shift_out); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_00[i], in_00[i], mask); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_10[i], in_10[i], mask); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_01[i], in_01[i], mask); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_11[i], in_11[i], mask); } }
Основной цикл на ассемблере
.L5516: { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=4, asz=4, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=3, asz=4, abs=0, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=2, asz=4, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=1, asz=4, abs=16, disp=0 } .L5255: { loop_mode bitrevd,0,sm %b[4], %b[1] qppermb,1,sm %b[27], %b[27], %r7, %b[9] stqp,2 %r2, %b[14], %b[28] shld,3,sm %b[24], 0x4, %b[12] qppermb,4,sm %b[22], %b[22], %r7, %b[5] stqp,5 %r4, %b[14], %b[26] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[19], %b[19], %r7, %b[26] addd,1,sm %b[2], 0x1, %b[0] stqp,2 %r0, %b[14], %b[11] qppermb,3,sm %b[10], %b[10], %r7, %b[24] shrd,4,sm %b[3], %r6, %b[22] stqp,5 %r5, %b[14], %b[7] movaqp,0 area=1, ind=0, am=1, be=0, %b[4] movaqp,1 area=0, ind=0, am=1, be=0, %b[13] movaqp,2 area=1, ind=0, am=1, be=0, %b[16] movaqp,3 area=0, ind=0, am=1, be=0, %b[21] }
Теоретическая скорость: 8 комплексных чисел за 2 такта (8/2) = 32 Байт/такт
Замеры скорости

Видим замедление в начале и ускорение в конце графика.
6. reverse_radix2_vector2_stream8
Чтение в 8 потоков из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix2_vector2_stream8(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_000 = (__v2di*)&data_in[0 * count_in/8]; __v2di *in_001 = (__v2di*)&data_in[1 * count_in/8]; __v2di *in_010 = (__v2di*)&data_in[2 * count_in/8]; __v2di *in_011 = (__v2di*)&data_in[3 * count_in/8]; __v2di *in_100 = (__v2di*)&data_in[4 * count_in/8]; __v2di *in_101 = (__v2di*)&data_in[5 * count_in/8]; __v2di *in_110 = (__v2di*)&data_in[6 * count_in/8]; __v2di *in_111 = (__v2di*)&data_in[7 * count_in/8]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/8; ++i) { int64_t offset = 16 * (__builtin_e2k_bitrevd(i) >> shift_out); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_000[i], in_000[i], mask); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_100[i], in_100[i], mask); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_010[i], in_010[i], mask); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_110[i], in_110[i], mask); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_001[i], in_001[i], mask); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_101[i], in_101[i], mask); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_011[i], in_011[i], mask); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_111[i], in_111[i], mask); } }
Основной цикл на ассемблере
.L6024: { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=8, asz=3, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=7, asz=3, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=6, asz=3, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=5, asz=3, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=4, asz=3, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=3, asz=3, abs=16, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=2, asz=3, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=1, asz=3, abs=24, disp=0 } .L5591: { loop_mode qppermb,1,sm %b[21], %b[21], %r11, %b[26] stqp,2 %r2, %b[18], %b[24] qppermb,4,sm %b[13], %b[13], %r11, %b[25] stqp,5 %r4, %b[18], %b[23] } { loop_mode qppermb,1,sm %b[12], %b[12], %r11, %b[24] stqp,2 %r0, %b[18], %b[26] shrd,3,sm %b[22], %r12, %b[1] qppermb,4,sm %b[5], %b[5], %r11, %b[23] stqp,5 %r5, %b[18], %b[25] } { loop_mode qppermb,1,sm %b[9], %b[9], %r11, %b[27] stqp,2 %r6, %b[18], %b[24] shld,3,sm %b[1], 0x4, %b[16] qppermb,4,sm %b[8], %b[8], %r11, %b[25] stqp,5 %r9, %b[18], %b[23] movaqp,0 area=1, ind=0, am=1, be=0, %b[12] movaqp,1 area=0, ind=0, am=1, be=0, %b[13] movaqp,2 area=3, ind=0, am=1, be=0, %b[4] movaqp,3 area=2, ind=0, am=1, be=0, %b[5] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[15], %b[15], %r11, %b[22] addd,1,sm %b[2], 0x1, %b[0] stqp,2 %r7, %b[18], %b[27] qppermb,3,sm %b[14], %b[14], %r11, %b[21] bitrevd,4,sm %b[2], %b[20] stqp,5 %r10, %b[18], %b[25] movaqp,0 area=3, ind=0, am=1, be=0, %b[1] movaqp,1 area=2, ind=0, am=1, be=0, %b[8] movaqp,2 area=1, ind=0, am=1, be=0, %b[9] movaqp,3 area=0, ind=0, am=1, be=0, %b[17] }
Теоретическая скорость: 16 комплексных чисел за 4 такта (16/4) = 32 Байт/такт
Замеры скорости

Видим сильное ускорение.
7. reverse_radix2_vector2_stream16
Чтение в 16 потоков из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix2_vector2_stream16(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_0000 = (__v2di*)&data_in[ 0 * count_in/16]; __v2di *in_0001 = (__v2di*)&data_in[ 1 * count_in/16]; __v2di *in_0010 = (__v2di*)&data_in[ 2 * count_in/16]; __v2di *in_0011 = (__v2di*)&data_in[ 3 * count_in/16]; __v2di *in_0100 = (__v2di*)&data_in[ 4 * count_in/16]; __v2di *in_0101 = (__v2di*)&data_in[ 5 * count_in/16]; __v2di *in_0110 = (__v2di*)&data_in[ 6 * count_in/16]; __v2di *in_0111 = (__v2di*)&data_in[ 7 * count_in/16]; __v2di *in_1000 = (__v2di*)&data_in[ 8 * count_in/16]; __v2di *in_1001 = (__v2di*)&data_in[ 9 * count_in/16]; __v2di *in_1010 = (__v2di*)&data_in[10 * count_in/16]; __v2di *in_1011 = (__v2di*)&data_in[11 * count_in/16]; __v2di *in_1100 = (__v2di*)&data_in[12 * count_in/16]; __v2di *in_1101 = (__v2di*)&data_in[13 * count_in/16]; __v2di *in_1110 = (__v2di*)&data_in[14 * count_in/16]; __v2di *in_1111 = (__v2di*)&data_in[15 * count_in/16]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/16; ++i) { int64_t offset = 16 * (__builtin_e2k_bitrevd(i) >> shift_out); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_0000[i], in_0000[i], mask); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_1000[i], in_1000[i], mask); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_0100[i], in_0100[i], mask); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_1100[i], in_1100[i], mask); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_0010[i], in_0010[i], mask); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_1010[i], in_1010[i], mask); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_0110[i], in_0110[i], mask); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_1110[i], in_1110[i], mask); *(__v2du*)((void*)data_out + offset + 8*16) = __builtin_e2k_qppermb(in_0001[i], in_0001[i], mask); *(__v2du*)((void*)data_out + offset + 9*16) = __builtin_e2k_qppermb(in_1001[i], in_1001[i], mask); *(__v2du*)((void*)data_out + offset + 10*16) = __builtin_e2k_qppermb(in_0101[i], in_0101[i], mask); *(__v2du*)((void*)data_out + offset + 11*16) = __builtin_e2k_qppermb(in_1101[i], in_1101[i], mask); *(__v2du*)((void*)data_out + offset + 12*16) = __builtin_e2k_qppermb(in_0011[i], in_0011[i], mask); *(__v2du*)((void*)data_out + offset + 13*16) = __builtin_e2k_qppermb(in_1011[i], in_1011[i], mask); *(__v2du*)((void*)data_out + offset + 14*16) = __builtin_e2k_qppermb(in_0111[i], in_0111[i], mask); *(__v2du*)((void*)data_out + offset + 15*16) = __builtin_e2k_qppermb(in_1111[i], in_1111[i], mask); } }
Основной цикл на ассемблере
.L6935: { fapb ct=0, dcd=0, fmt=5, mrng=16, d=1, incr=0, ind=0, asz=2, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=15, asz=2, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=14, asz=2, abs=4, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=13, asz=2, abs=4, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=12, asz=2, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=11, asz=2, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=10, asz=2, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=9, asz=2, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=8, asz=2, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=7, asz=2, abs=16, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=6, asz=2, abs=20, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=5, asz=2, abs=20, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=4, asz=2, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=3, asz=2, abs=24, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=2, asz=2, abs=28, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=1, asz=2, abs=28, disp=0 } .L6163: { loop_mode qppermb,1,sm %b[24], %b[24], %r1, %b[28] stqp,2 %r2, %b[13], %b[9] qppermb,4,sm %b[22], %b[22], %r1, %b[27] stqp,5 %r5, %b[13], %b[4] } { loop_mode qppermb,1,sm %b[26], %b[26], %r1, %b[30] stqp,2 %r0, %b[13], %b[28] qppermb,4,sm %b[25], %b[25], %r1, %b[29] stqp,5 %r6, %b[13], %b[27] movaqp,0 area=7, ind=0, am=1, be=0, %b[10] movaqp,1 area=6, ind=0, am=1, be=0, %b[4] movaqp,2 area=7, ind=0, am=1, be=0, %b[7] movaqp,3 area=6, ind=0, am=1, be=0, %b[1] } { loop_mode qppermb,1,sm %b[23], %b[23], %r1, %b[27] stqp,2 %r7, %b[13], %b[30] qppermb,4,sm %b[16], %b[16], %r1, %b[26] stqp,5 %r10, %b[13], %b[29] movaqp,0 area=1, ind=0, am=1, be=0, %b[15] movaqp,1 area=0, ind=0, am=1, be=0, %b[17] movaqp,2 area=5, ind=0, am=1, be=0, %b[9] movaqp,3 area=4, ind=0, am=1, be=0, %b[12] } { loop_mode qppermb,1,sm %b[21], %b[21], %r1, %b[29] stqp,2 %r9, %b[13], %b[27] qppermb,4,sm %b[14], %b[14], %r1, %b[28] stqp,5 %r11, %b[13], %b[26] movaqp,0 area=5, ind=0, am=1, be=0, %b[16] movaqp,1 area=4, ind=0, am=1, be=0, %b[19] movaqp,2 area=1, ind=0, am=1, be=0, %b[20] movaqp,3 area=0, ind=0, am=1, be=0, %b[22] } { loop_mode addd,0,sm %b[2], 0x1, %b[0] qppermb,1,sm %b[18], %b[18], %r1, %b[27] stqp,2 %r12, %b[13], %b[29] bitrevd,3,sm %b[2], %b[25] qppermb,4,sm %b[11], %b[11], %r1, %b[26] stqp,5 %r13, %b[13], %b[28] movaqp,0 area=3, ind=0, am=1, be=0, %b[23] movaqp,1 area=2, ind=0, am=1, be=0, %b[24] movaqp,2 area=3, ind=0, am=1, be=0, %b[14] movaqp,3 area=2, ind=0, am=1, be=0, %b[21] } { loop_mode qppermb,1,sm %b[6], %b[6], %r1, %b[29] stqp,2 %r14, %b[13], %b[27] shrd,3,sm %b[25], %r20, %b[2] qppermb,4,sm %b[3], %b[3], %r1, %b[28] stqp,5 %r15, %b[13], %b[26] } { loop_mode qppermb,1,sm %b[10], %b[10], %r1, %b[6] stqp,2 %r16, %b[13], %b[29] shld,3,sm %b[2], 0x4, %b[11] qppermb,4,sm %b[7], %b[7], %r1, %b[3] stqp,5 %r17, %b[13], %b[28] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[17], %b[17], %r1, %b[7] stqp,2 %r18, %b[13], %b[8] qppermb,3,sm %b[15], %b[15], %r1, %b[2] stqp,5 %r19, %b[13], %b[5] }
Теоретическая скорость: 32 комплексных числа за 8 тактов (32/8) = 32 Байт/такт
Замеры скорости

Видим сильное ускорение.
8. reverse_radix2_vector2_stream32
Чтение в 32 потока из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix2_vector2_stream32(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_00000 = (__v2di*)&data_in[ 0 * count_in/32]; __v2di *in_00001 = (__v2di*)&data_in[ 1 * count_in/32]; __v2di *in_00010 = (__v2di*)&data_in[ 2 * count_in/32]; __v2di *in_00011 = (__v2di*)&data_in[ 3 * count_in/32]; __v2di *in_00100 = (__v2di*)&data_in[ 4 * count_in/32]; __v2di *in_00101 = (__v2di*)&data_in[ 5 * count_in/32]; __v2di *in_00110 = (__v2di*)&data_in[ 6 * count_in/32]; __v2di *in_00111 = (__v2di*)&data_in[ 7 * count_in/32]; __v2di *in_01000 = (__v2di*)&data_in[ 8 * count_in/32]; __v2di *in_01001 = (__v2di*)&data_in[ 9 * count_in/32]; __v2di *in_01010 = (__v2di*)&data_in[10 * count_in/32]; __v2di *in_01011 = (__v2di*)&data_in[11 * count_in/32]; __v2di *in_01100 = (__v2di*)&data_in[12 * count_in/32]; __v2di *in_01101 = (__v2di*)&data_in[13 * count_in/32]; __v2di *in_01110 = (__v2di*)&data_in[14 * count_in/32]; __v2di *in_01111 = (__v2di*)&data_in[15 * count_in/32]; __v2di *in_10000 = (__v2di*)&data_in[16 * count_in/32]; __v2di *in_10001 = (__v2di*)&data_in[17 * count_in/32]; __v2di *in_10010 = (__v2di*)&data_in[18 * count_in/32]; __v2di *in_10011 = (__v2di*)&data_in[19 * count_in/32]; __v2di *in_10100 = (__v2di*)&data_in[20 * count_in/32]; __v2di *in_10101 = (__v2di*)&data_in[21 * count_in/32]; __v2di *in_10110 = (__v2di*)&data_in[22 * count_in/32]; __v2di *in_10111 = (__v2di*)&data_in[23 * count_in/32]; __v2di *in_11000 = (__v2di*)&data_in[24 * count_in/32]; __v2di *in_11001 = (__v2di*)&data_in[25 * count_in/32]; __v2di *in_11010 = (__v2di*)&data_in[26 * count_in/32]; __v2di *in_11011 = (__v2di*)&data_in[27 * count_in/32]; __v2di *in_11100 = (__v2di*)&data_in[28 * count_in/32]; __v2di *in_11101 = (__v2di*)&data_in[29 * count_in/32]; __v2di *in_11110 = (__v2di*)&data_in[30 * count_in/32]; __v2di *in_11111 = (__v2di*)&data_in[31 * count_in/32]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/32; ++i) { int64_t offset = 16 * (__builtin_e2k_bitrevd(i) >> shift_out); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_00000[i], in_00000[i], mask); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_10000[i], in_10000[i], mask); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_01000[i], in_01000[i], mask); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_11000[i], in_11000[i], mask); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_00100[i], in_00100[i], mask); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_10100[i], in_10100[i], mask); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_01100[i], in_01100[i], mask); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_11100[i], in_11100[i], mask); *(__v2du*)((void*)data_out + offset + 8*16) = __builtin_e2k_qppermb(in_00010[i], in_00010[i], mask); *(__v2du*)((void*)data_out + offset + 9*16) = __builtin_e2k_qppermb(in_10010[i], in_10010[i], mask); *(__v2du*)((void*)data_out + offset + 10*16) = __builtin_e2k_qppermb(in_01010[i], in_01010[i], mask); *(__v2du*)((void*)data_out + offset + 11*16) = __builtin_e2k_qppermb(in_11010[i], in_11010[i], mask); *(__v2du*)((void*)data_out + offset + 12*16) = __builtin_e2k_qppermb(in_00110[i], in_00110[i], mask); *(__v2du*)((void*)data_out + offset + 13*16) = __builtin_e2k_qppermb(in_10110[i], in_10110[i], mask); *(__v2du*)((void*)data_out + offset + 14*16) = __builtin_e2k_qppermb(in_01110[i], in_01110[i], mask); *(__v2du*)((void*)data_out + offset + 15*16) = __builtin_e2k_qppermb(in_11110[i], in_11110[i], mask); *(__v2du*)((void*)data_out + offset + 16*16) = __builtin_e2k_qppermb(in_00001[i], in_00001[i], mask); *(__v2du*)((void*)data_out + offset + 17*16) = __builtin_e2k_qppermb(in_10001[i], in_10001[i], mask); *(__v2du*)((void*)data_out + offset + 18*16) = __builtin_e2k_qppermb(in_01001[i], in_01001[i], mask); *(__v2du*)((void*)data_out + offset + 19*16) = __builtin_e2k_qppermb(in_11001[i], in_11001[i], mask); *(__v2du*)((void*)data_out + offset + 20*16) = __builtin_e2k_qppermb(in_00101[i], in_00101[i], mask); *(__v2du*)((void*)data_out + offset + 21*16) = __builtin_e2k_qppermb(in_10101[i], in_10101[i], mask); *(__v2du*)((void*)data_out + offset + 22*16) = __builtin_e2k_qppermb(in_01101[i], in_01101[i], mask); *(__v2du*)((void*)data_out + offset + 23*16) = __builtin_e2k_qppermb(in_11101[i], in_11101[i], mask); *(__v2du*)((void*)data_out + offset + 24*16) = __builtin_e2k_qppermb(in_00011[i], in_00011[i], mask); *(__v2du*)((void*)data_out + offset + 25*16) = __builtin_e2k_qppermb(in_10011[i], in_10011[i], mask); *(__v2du*)((void*)data_out + offset + 26*16) = __builtin_e2k_qppermb(in_01011[i], in_01011[i], mask); *(__v2du*)((void*)data_out + offset + 27*16) = __builtin_e2k_qppermb(in_11011[i], in_11011[i], mask); *(__v2du*)((void*)data_out + offset + 28*16) = __builtin_e2k_qppermb(in_00111[i], in_00111[i], mask); *(__v2du*)((void*)data_out + offset + 29*16) = __builtin_e2k_qppermb(in_10111[i], in_10111[i], mask); *(__v2du*)((void*)data_out + offset + 30*16) = __builtin_e2k_qppermb(in_01111[i], in_01111[i], mask); *(__v2du*)((void*)data_out + offset + 31*16) = __builtin_e2k_qppermb(in_11111[i], in_11111[i], mask); } }
Основной цикл на ассемблере
.L8688: { fapb ct=0, dcd=0, fmt=5, mrng=16, d=17, incr=0, ind=0, asz=1, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=16, incr=0, ind=0, asz=1, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=15, incr=0, ind=0, asz=1, abs=2, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=14, incr=0, ind=0, asz=1, abs=2, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=13, incr=0, ind=0, asz=1, abs=4, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=12, incr=0, ind=0, asz=1, abs=4, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=11, incr=0, ind=0, asz=1, abs=6, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=10, incr=0, ind=0, asz=1, abs=6, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=9, incr=0, ind=0, asz=1, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=8, incr=0, ind=0, asz=1, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=7, incr=0, ind=0, asz=1, abs=10, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=6, incr=0, ind=0, asz=1, abs=10, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=5, incr=0, ind=0, asz=1, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=4, incr=0, ind=0, asz=1, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=3, incr=0, ind=0, asz=1, abs=14, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=2, incr=0, ind=0, asz=1, abs=14, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=1, incr=0, ind=0, asz=1, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=15, asz=1, abs=16, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=14, asz=1, abs=18, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=13, asz=1, abs=18, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=12, asz=1, abs=20, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=11, asz=1, abs=20, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=10, asz=1, abs=22, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=9, asz=1, abs=22, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=8, asz=1, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=7, asz=1, abs=24, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=6, asz=1, abs=26, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=5, asz=1, abs=26, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=4, asz=1, abs=28, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=3, asz=1, abs=28, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=2, asz=1, abs=30, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=1, asz=1, abs=30, disp=0 } .L7202: { loop_mode qppermb,1,sm %b[37], %b[37], %r0, %b[21] stqp,2 %r2, %b[16], %b[20] qppermb,4,sm %b[35], %b[35], %r0, %b[18] stqp,5 %r6, %b[16], %b[18] } { loop_mode qppermb,1,sm %b[39], %b[39], %r0, %b[20] stqp,2 %r4, %b[16], %b[21] qppermb,4,sm %b[38], %b[38], %r0, %b[18] stqp,5 %r7, %b[16], %b[18] } { loop_mode qppermb,1,sm %b[23], %b[23], %r0, %b[19] stqp,2 %r9, %b[16], %b[20] qppermb,4,sm %b[19], %b[19], %r0, %b[18] stqp,5 %r11, %b[16], %b[18] } { loop_mode qppermb,1,sm %b[26], %b[26], %r0, %b[19] stqp,2 %r10, %b[16], %b[19] qppermb,4,sm %b[33], %b[33], %r0, %b[18] stqp,5 %r12, %b[16], %b[18] } { loop_mode qppermb,1,sm %b[25], %b[25], %r0, %b[19] stqp,2 %r13, %b[16], %b[19] qppermb,4,sm %b[32], %b[32], %r0, %b[18] stqp,5 %r14, %b[16], %b[18] } { loop_mode qppermb,1,sm %b[31], %b[31], %r0, %b[24] stqp,2 %r15, %b[16], %b[19] qppermb,4,sm %b[30], %b[30], %r0, %b[21] stqp,5 %r16, %b[16], %b[18] movaqp,0 area=15, ind=0, am=1, be=0, %b[19] movaqp,1 area=14, ind=0, am=1, be=0, %b[22] movaqp,2 area=15, ind=0, am=1, be=0, %b[18] movaqp,3 area=14, ind=0, am=1, be=0, %b[20] } { loop_mode qppermb,1,sm %b[29], %b[29], %r0, %b[26] stqp,2 %r17, %b[16], %b[24] qppermb,4,sm %b[17], %b[17], %r0, %b[25] stqp,5 %r18, %b[16], %b[21] movaqp,0 area=13, ind=0, am=1, be=0, %b[21] movaqp,1 area=12, ind=0, am=1, be=0, %b[24] movaqp,2 area=13, ind=0, am=1, be=0, %b[17] movaqp,3 area=12, ind=0, am=1, be=0, %b[23] } { loop_mode qppermb,1,sm %b[28], %b[28], %r0, %b[26] stqp,2 %r19, %b[16], %b[26] qppermb,4,sm %b[27], %b[27], %r0, %b[25] stqp,5 %r20, %b[16], %b[25] movaqp,0 area=11, ind=0, am=1, be=0, %b[1] movaqp,1 area=10, ind=0, am=1, be=0, %b[13] movaqp,2 area=11, ind=0, am=1, be=0, %b[0] movaqp,3 area=10, ind=0, am=1, be=0, %b[12] } { loop_mode qppermb,1,sm %g17, %g17, %r0, %b[28] stqp,2 %r21, %b[16], %b[26] qppermb,4,sm %g16, %g16, %r0, %b[27] stqp,5 %r22, %b[16], %b[25] movaqp,0 area=9, ind=0, am=1, be=0, %g17 movaqp,1 area=8, ind=0, am=1, be=0, %b[26] movaqp,2 area=9, ind=0, am=1, be=0, %g16 movaqp,3 area=8, ind=0, am=1, be=0, %b[25] } { loop_mode qppermb,1,sm %b[15], %b[15], %r0, %b[31] stqp,2 %r23, %b[16], %b[28] qppermb,4,sm %b[14], %b[14], %r0, %b[30] stqp,5 %r24, %b[16], %b[27] movaqp,0 area=7, ind=0, am=1, be=0, %b[27] movaqp,1 area=6, ind=0, am=1, be=0, %b[29] movaqp,2 area=7, ind=0, am=1, be=0, %b[15] movaqp,3 area=6, ind=0, am=1, be=0, %b[28] } { loop_mode qppermb,1,sm %b[3], %b[3], %r0, %b[36] stqp,2 %r25, %b[16], %b[31] qppermb,4,sm %b[2], %b[2], %r0, %b[33] stqp,5 %r26, %b[16], %b[30] movaqp,0 area=1, ind=0, am=1, be=0, %b[32] movaqp,1 area=0, ind=0, am=1, be=0, %b[34] movaqp,2 area=5, ind=0, am=1, be=0, %b[30] movaqp,3 area=4, ind=0, am=1, be=0, %b[31] } { loop_mode qppermb,1,sm %b[24], %b[24], %r0, %b[3] stqp,2 %r27, %b[16], %b[36] qppermb,4,sm %b[23], %b[23], %r0, %b[2] stqp,5 %r28, %b[16], %b[33] movaqp,0 area=5, ind=0, am=1, be=0, %b[23] movaqp,1 area=4, ind=0, am=1, be=0, %b[24] movaqp,2 area=1, ind=0, am=1, be=0, %b[33] movaqp,3 area=0, ind=0, am=1, be=0, %b[35] } { loop_mode addd,0,sm %r5, 0x1, %r5 qppermb,1,sm %b[21], %b[21], %r0, %b[5] stqp,2 %r29, %b[16], %b[5] bitrevd,3,sm %r5, %b[38] qppermb,4,sm %b[17], %b[17], %r0, %b[4] stqp,5 %r30, %b[16], %b[4] movaqp,0 area=3, ind=0, am=1, be=0, %b[36] movaqp,1 area=2, ind=0, am=1, be=0, %b[37] movaqp,2 area=3, ind=0, am=1, be=0, %b[17] movaqp,3 area=2, ind=0, am=1, be=0, %b[21] } { loop_mode qppermb,1,sm %b[22], %b[22], %r0, %b[7] stqp,2 %r31, %b[16], %b[7] shrd,3,sm %b[38], %r1, %b[20] qppermb,4,sm %b[20], %b[20], %r0, %b[6] stqp,5 %r32, %b[16], %b[6] } { loop_mode qppermb,1,sm %b[19], %b[19], %r0, %b[9] stqp,2 %r33, %b[16], %b[9] shld,3,sm %b[20], 0x4, %b[14] qppermb,4,sm %b[18], %b[18], %r0, %b[8] stqp,5 %r34, %b[16], %b[8] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[34], %b[34], %r0, %b[18] stqp,2 %r35, %b[16], %b[11] qppermb,3,sm %b[32], %b[32], %r0, %b[16] stqp,5 %r36, %b[16], %b[10] }
Теоретическая скорость: 64 комплексных числа за 16 тактов (64/16) = 32 Байт/такт
Замеры скорости

Видим замедление по всей длине графика.
При попытке чтения в 64 потока получается резко менее эффективный код.
APB не может читать в 64 потока, поэтому в дополнение к APB компилятор вставляет обычные операции чтения ldqp. В итоге скорость резко проседает.
Итоги по reverse_radix2_vector2


Победителем можно считать reverse_radix2_vector2_stream16.
При реализации Vector-2 Radix-2 FFT будем использовать его.
reverse_radix2_vector4
1. reverse_radix2_vector4_etalon
Эталонный вариант для сравнения на корректность.
Здесь reverseNumber_radix2 вычисляется с помощью цикла.
int reverseNumber_radix2(int number, int bit_count) { int answer = 0; for(int i = 0; i < bit_count; ++i) { answer <<= 1; answer |= number & 1; number >>= 1; } return answer; } void reverse_radix2_vector4_etalon(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; for(int64_t i = 0; i < count_out; ++i) { int index = reverseNumber_radix2(i, bit_count_out); myComplex a = data_in[4*i + 0]; myComplex b = data_in[4*i + 1]; myComplex c = data_in[4*i + 2]; myComplex d = data_in[4*i + 3]; data_out[index] = (myComplex4){.real = {a.real, b.real, c.real, d.real}, .imag = {a.imag, b.imag, c.imag, d.imag}}; } }
2. reverse_radix2_vector4
В процессоре есть инструкция bitrevd, которая производит операцию reverseNumber_radix2 над 64-битным числом. Заменим reverseNumber_radix2() на __builtin_e2k_bitrevd().
Код на Си
void reverse_radix2_vector4(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; int64_t delta = (1LL << shift_out) / 32; __v2di *in = (__v2di*)data_in; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0, shifted_i = 0; i < count_out; ++i, shifted_i += delta) { int64_t offset = __builtin_e2k_bitrevd(shifted_i); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in[2*i+1], in[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in[2*i+1], in[2*i+0], mask_imag); } }
Основной цикл на ассемблере
.L6535: { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 } .L6346: { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END bitrevd,0,sm %b[22], %b[13] qppermb,1,sm %b[11], %b[10], %r5, %b[19] stqp,2 %r0, %b[17], %b[21] qppermb,3,sm %b[11], %b[10], %r6, %b[23] addd,4,sm %b[20], %r4, %b[18] stqp,5 %r2, %b[17], %b[25] movaqp,0 area=0, ind=0, am=1, be=0, %b[0] movaqp,1 area=0, ind=16, am=0, be=0, %b[1] }
Теоретическая скорость: 4 комплексных числа за 1 такт (4/1) = 32 Байт/такт
Замеры скорости

3. reverse_radix2_vector4_x2
Сделаем ручную раскрутку цикла в 2 раза.
Код на Си
void reverse_radix2_vector4_x2(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in = (__v2di*) data_in; __v2di *out_0 = (__v2di*)&data_out[0]; __v2di *out_1 = (__v2di*)&data_out[count_out/2]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out; i += 2) { int64_t offset = 32 * (__builtin_e2k_bitrevd(i) >> shift_out); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)out_0 + offset + 0*16) = __builtin_e2k_qppermb(in[2*(i+0)+1], in[2*(i+0)+0], mask_real); *(__v2du*)((void*)out_0 + offset + 1*16) = __builtin_e2k_qppermb(in[2*(i+0)+1], in[2*(i+0)+0], mask_imag); *(__v2du*)((void*)out_1 + offset + 0*16) = __builtin_e2k_qppermb(in[2*(i+1)+1], in[2*(i+1)+0], mask_real); *(__v2du*)((void*)out_1 + offset + 1*16) = __builtin_e2k_qppermb(in[2*(i+1)+1], in[2*(i+1)+0], mask_imag); } }
Основной цикл на ассемблере
.L6884: { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=32 } .L6561: { loop_mode bitrevd,0,sm %b[4], %b[1] qppermb,1,sm %b[25], %b[20], %r7, %b[28] stqp,2 %r0, %b[7], %b[26] shld,3,sm %b[22], 0x5, %b[5] qppermb,4,sm %b[17], %b[12], %r7, %b[27] stqp,5 %r4, %b[7], %b[24] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[23], %b[18], %r9, %b[24] addd,1,sm %b[2], 0x2, %b[0] stqp,2 %r2, %b[7], %b[28] qppermb,3,sm %b[15], %b[10], %r9, %b[22] shrd,4,sm %b[3], %r6, %b[20] stqp,5 %r5, %b[7], %b[27] movaqp,0 area=0, ind=0, am=1, be=0, %b[12] movaqp,1 area=0, ind=16, am=0, be=0, %b[17] movaqp,2 area=0, ind=0, am=1, be=0, %b[4] movaqp,3 area=0, ind=16, am=0, be=0, %b[9] }
Теоретическая скорость: 8 комплексных чисел за 2 такта (8/2) = 32 Байт/такт
Замеры скорости

Видим ускорение в начале графика.
Здесь происходит два чтения из одного места памяти и запись в два разных места памяти.
4. reverse_radix2_vector4_stream2
Теперь сделаем наоборот: будем читать из двух разных мест, а писать рядом.
Чтение в 2 потока из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix2_vector4_stream2(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_0 = (__v2di*)&data_in[0]; __v2di *in_1 = (__v2di*)&data_in[count_in/2]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/2; ++i) { int64_t offset = 32 * (__builtin_e2k_bitrevd(i) >> shift_out); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_0[2*i+1], in_0[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_0[2*i+1], in_0[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_1[2*i+1], in_1[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_1[2*i+1], in_1[2*i+0], mask_imag); } }
Основной цикл на ассемблере
.L7199: { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=2, asz=5, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 } .L6909: { loop_mode bitrevd,0,sm %b[4], %b[1] qppermb,1,sm %b[25], %b[20], %r9, %b[28] stqp,2 %r0, %b[7], %b[26] shld,3,sm %b[22], 0x5, %b[5] qppermb,4,sm %b[17], %b[12], %r9, %b[27] stqp,5 %r5, %b[7], %b[24] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[23], %b[18], %r6, %b[24] addd,1,sm %b[2], 0x1, %b[0] stqp,2 %r2, %b[7], %b[28] qppermb,3,sm %b[15], %b[10], %r6, %b[22] shrd,4,sm %b[3], %r7, %b[20] stqp,5 %r4, %b[7], %b[27] movaqp,0 area=0, ind=0, am=1, be=0, %b[12] movaqp,1 area=0, ind=16, am=0, be=0, %b[17] movaqp,2 area=0, ind=0, am=1, be=0, %b[4] movaqp,3 area=0, ind=16, am=0, be=0, %b[9] }
Теоретическая скорость: 8 комплексных чисел за 2 такта (8/2) = 32 Байт/такт
Замеры скорости

Видим замедление в начале и ускорение в конце графика.
5. reverse_radix2_vector4_stream4
Чтение в 4 потока из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix2_vector4_stream4(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_00 = (__v2di*)&data_in[0 * count_in/4]; __v2di *in_01 = (__v2di*)&data_in[1 * count_in/4]; __v2di *in_10 = (__v2di*)&data_in[2 * count_in/4]; __v2di *in_11 = (__v2di*)&data_in[3 * count_in/4]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/4; ++i) { int64_t offset = 32 * (__builtin_e2k_bitrevd(i) >> shift_out); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_00[2*i+1], in_00[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_00[2*i+1], in_00[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_10[2*i+1], in_10[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_10[2*i+1], in_10[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_01[2*i+1], in_01[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_01[2*i+1], in_01[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_11[2*i+1], in_11[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_11[2*i+1], in_11[2*i+0], mask_imag); } }
Основной цикл на ассемблере
.L7728: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=4, asz=4, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=3, asz=4, abs=0, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=2, asz=4, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=4, abs=16, disp=0 } .L7242: { loop_mode qppermb,1,sm %b[20], %b[16], %r13, %b[26] stqp,2 %r0, %b[21], %b[25] qppermb,4,sm %b[12], %b[8], %r13, %b[24] stqp,5 %r5, %b[21], %b[22] } { loop_mode qppermb,1,sm %b[17], %b[13], %r12, %b[25] stqp,2 %r2, %b[21], %b[26] shrd,3,sm %b[19], %r11, %b[1] qppermb,4,sm %b[9], %b[5], %r12, %b[22] stqp,5 %r4, %b[21], %b[24] } { loop_mode qppermb,1,sm %b[17], %b[13], %r13, %b[27] stqp,2 %r7, %b[21], %b[25] shld,3,sm %b[1], 0x5, %b[19] qppermb,4,sm %b[9], %b[5], %r13, %b[24] stqp,5 %r10, %b[21], %b[22] movaqp,0 area=0, ind=0, am=1, be=0, %b[12] movaqp,1 area=0, ind=16, am=0, be=0, %b[16] movaqp,2 area=0, ind=0, am=1, be=0, %b[4] movaqp,3 area=0, ind=16, am=0, be=0, %b[8] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[18], %b[14], %r12, %b[23] addd,1,sm %b[2], 0x1, %b[0] stqp,2 %r6, %b[21], %b[27] qppermb,3,sm %b[10], %b[6], %r12, %b[20] bitrevd,4,sm %b[2], %b[17] stqp,5 %r9, %b[21], %b[24] movaqp,0 area=1, ind=0, am=1, be=0, %b[9] movaqp,1 area=1, ind=16, am=0, be=0, %b[13] movaqp,2 area=1, ind=0, am=1, be=0, %b[1] movaqp,3 area=1, ind=16, am=0, be=0, %b[5] }
Теоретическая скорость: 16 комплексных чисел за 4 такта (16/4) = 32 Байт/такт
Замеры скорости

Видим сильное ускорение.
6. reverse_radix2_vector4_stream8
Чтение в 8 потоков из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix2_vector4_stream8(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_000 = (__v2di*)&data_in[0 * count_in/8]; __v2di *in_001 = (__v2di*)&data_in[1 * count_in/8]; __v2di *in_010 = (__v2di*)&data_in[2 * count_in/8]; __v2di *in_011 = (__v2di*)&data_in[3 * count_in/8]; __v2di *in_100 = (__v2di*)&data_in[4 * count_in/8]; __v2di *in_101 = (__v2di*)&data_in[5 * count_in/8]; __v2di *in_110 = (__v2di*)&data_in[6 * count_in/8]; __v2di *in_111 = (__v2di*)&data_in[7 * count_in/8]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/8; ++i) { int64_t offset = 32 * (__builtin_e2k_bitrevd(i) >> shift_out); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_000[2*i+1], in_000[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_000[2*i+1], in_000[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_100[2*i+1], in_100[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_100[2*i+1], in_100[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_010[2*i+1], in_010[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_010[2*i+1], in_010[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_110[2*i+1], in_110[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_110[2*i+1], in_110[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 8*16) = __builtin_e2k_qppermb(in_001[2*i+1], in_001[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 9*16) = __builtin_e2k_qppermb(in_001[2*i+1], in_001[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 10*16) = __builtin_e2k_qppermb(in_101[2*i+1], in_101[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 11*16) = __builtin_e2k_qppermb(in_101[2*i+1], in_101[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 12*16) = __builtin_e2k_qppermb(in_011[2*i+1], in_011[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 13*16) = __builtin_e2k_qppermb(in_011[2*i+1], in_011[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 14*16) = __builtin_e2k_qppermb(in_111[2*i+1], in_111[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 15*16) = __builtin_e2k_qppermb(in_111[2*i+1], in_111[2*i+0], mask_imag); } }
Основной цикл на ассемблере
.L8681: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=8, asz=3, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=7, asz=3, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=6, asz=3, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=5, asz=3, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=4, asz=3, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=3, asz=3, abs=16, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=2, asz=3, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=3, abs=24, disp=0 } .L7803: { loop_mode qppermb,1,sm %b[16], %b[15], %r20, %b[28] stqp,2 %r0, %b[9], %b[5] qppermb,4,sm %b[14], %b[13], %r20, %b[27] stqp,5 %r6, %b[9], %b[4] } { loop_mode qppermb,1,sm %b[26], %b[25], %r21, %b[30] stqp,2 %r2, %b[9], %b[28] qppermb,4,sm %b[24], %b[23], %r21, %b[29] stqp,5 %r5, %b[9], %b[27] movaqp,0 area=3, ind=0, am=1, be=0, %b[5] movaqp,1 area=3, ind=16, am=0, be=0, %b[8] movaqp,2 area=3, ind=0, am=1, be=0, %b[1] movaqp,3 area=3, ind=16, am=0, be=0, %b[4] } { loop_mode qppermb,1,sm %b[26], %b[25], %r20, %b[28] stqp,2 %r9, %b[9], %b[30] qppermb,4,sm %b[24], %b[23], %r20, %b[27] stqp,5 %r11, %b[9], %b[29] movaqp,0 area=0, ind=0, am=1, be=0, %b[13] movaqp,1 area=0, ind=16, am=0, be=0, %b[14] movaqp,2 area=0, ind=0, am=1, be=0, %b[11] movaqp,3 area=0, ind=16, am=0, be=0, %b[12] } { loop_mode qppermb,1,sm %b[22], %b[21], %r20, %b[29] stqp,2 %r7, %b[9], %b[28] qppermb,4,sm %b[22], %b[21], %r21, %b[26] stqp,5 %r10, %b[9], %b[27] movaqp,0 area=2, ind=0, am=1, be=0, %b[19] movaqp,1 area=2, ind=16, am=0, be=0, %b[20] movaqp,2 area=2, ind=0, am=1, be=0, %b[15] movaqp,3 area=2, ind=16, am=0, be=0, %b[16] } { loop_mode addd,0,sm %b[2], 0x1, %b[0] qppermb,1,sm %b[18], %b[17], %r20, %b[28] stqp,2 %r12, %b[9], %b[29] bitrevd,3,sm %b[2], %b[25] qppermb,4,sm %b[18], %b[17], %r21, %b[27] stqp,5 %r13, %b[9], %b[26] movaqp,0 area=1, ind=0, am=1, be=0, %b[23] movaqp,1 area=1, ind=16, am=0, be=0, %b[24] movaqp,2 area=1, ind=0, am=1, be=0, %b[21] movaqp,3 area=1, ind=16, am=0, be=0, %b[22] } { loop_mode qppermb,1,sm %b[10], %b[7], %r20, %b[29] stqp,2 %r14, %b[9], %b[28] shrd,3,sm %b[25], %r22, %b[2] qppermb,4,sm %b[10], %b[7], %r21, %b[26] stqp,5 %r15, %b[9], %b[27] } { loop_mode qppermb,1,sm %b[6], %b[3], %r20, %b[27] stqp,2 %r16, %b[9], %b[29] shld,3,sm %b[2], 0x5, %b[7] qppermb,4,sm %b[6], %b[3], %r21, %b[25] stqp,5 %r17, %b[9], %b[26] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[14], %b[13], %r21, %b[3] stqp,2 %r18, %b[9], %b[27] qppermb,3,sm %b[12], %b[11], %r21, %b[2] stqp,5 %r19, %b[9], %b[25] }
Теоретическая скорость: 32 комплексных числа за 8 тактов (32/8) = 32 Байт/такт
Замеры скорости

Видим сильное ускорение.
7. reverse_radix2_vector4_stream16
Чтение в 16 потоков из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix2_vector4_stream16(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_0000 = (__v2di*)&data_in[ 0 * count_in/16]; __v2di *in_0001 = (__v2di*)&data_in[ 1 * count_in/16]; __v2di *in_0010 = (__v2di*)&data_in[ 2 * count_in/16]; __v2di *in_0011 = (__v2di*)&data_in[ 3 * count_in/16]; __v2di *in_0100 = (__v2di*)&data_in[ 4 * count_in/16]; __v2di *in_0101 = (__v2di*)&data_in[ 5 * count_in/16]; __v2di *in_0110 = (__v2di*)&data_in[ 6 * count_in/16]; __v2di *in_0111 = (__v2di*)&data_in[ 7 * count_in/16]; __v2di *in_1000 = (__v2di*)&data_in[ 8 * count_in/16]; __v2di *in_1001 = (__v2di*)&data_in[ 9 * count_in/16]; __v2di *in_1010 = (__v2di*)&data_in[10 * count_in/16]; __v2di *in_1011 = (__v2di*)&data_in[11 * count_in/16]; __v2di *in_1100 = (__v2di*)&data_in[12 * count_in/16]; __v2di *in_1101 = (__v2di*)&data_in[13 * count_in/16]; __v2di *in_1110 = (__v2di*)&data_in[14 * count_in/16]; __v2di *in_1111 = (__v2di*)&data_in[15 * count_in/16]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/16; ++i) { int64_t offset = 32 * (__builtin_e2k_bitrevd(i) >> shift_out); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_0000[2*i+1], in_0000[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_0000[2*i+1], in_0000[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_1000[2*i+1], in_1000[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_1000[2*i+1], in_1000[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_0100[2*i+1], in_0100[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_0100[2*i+1], in_0100[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_1100[2*i+1], in_1100[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_1100[2*i+1], in_1100[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 8*16) = __builtin_e2k_qppermb(in_0010[2*i+1], in_0010[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 9*16) = __builtin_e2k_qppermb(in_0010[2*i+1], in_0010[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 10*16) = __builtin_e2k_qppermb(in_1010[2*i+1], in_1010[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 11*16) = __builtin_e2k_qppermb(in_1010[2*i+1], in_1010[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 12*16) = __builtin_e2k_qppermb(in_0110[2*i+1], in_0110[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 13*16) = __builtin_e2k_qppermb(in_0110[2*i+1], in_0110[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 14*16) = __builtin_e2k_qppermb(in_1110[2*i+1], in_1110[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 15*16) = __builtin_e2k_qppermb(in_1110[2*i+1], in_1110[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 16*16) = __builtin_e2k_qppermb(in_0001[2*i+1], in_0001[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 17*16) = __builtin_e2k_qppermb(in_0001[2*i+1], in_0001[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 18*16) = __builtin_e2k_qppermb(in_1001[2*i+1], in_1001[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 19*16) = __builtin_e2k_qppermb(in_1001[2*i+1], in_1001[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 20*16) = __builtin_e2k_qppermb(in_0101[2*i+1], in_0101[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 21*16) = __builtin_e2k_qppermb(in_0101[2*i+1], in_0101[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 22*16) = __builtin_e2k_qppermb(in_1101[2*i+1], in_1101[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 23*16) = __builtin_e2k_qppermb(in_1101[2*i+1], in_1101[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 24*16) = __builtin_e2k_qppermb(in_0011[2*i+1], in_0011[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 25*16) = __builtin_e2k_qppermb(in_0011[2*i+1], in_0011[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 26*16) = __builtin_e2k_qppermb(in_1011[2*i+1], in_1011[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 27*16) = __builtin_e2k_qppermb(in_1011[2*i+1], in_1011[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 28*16) = __builtin_e2k_qppermb(in_0111[2*i+1], in_0111[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 29*16) = __builtin_e2k_qppermb(in_0111[2*i+1], in_0111[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 30*16) = __builtin_e2k_qppermb(in_1111[2*i+1], in_1111[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 31*16) = __builtin_e2k_qppermb(in_1111[2*i+1], in_1111[2*i+0], mask_imag); } }
Основной цикл на ассемблере
.L10479: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=1, incr=1, ind=0, asz=2, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=15, asz=2, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=14, asz=2, abs=4, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=13, asz=2, abs=4, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=12, asz=2, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=11, asz=2, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=10, asz=2, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=9, asz=2, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=8, asz=2, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=7, asz=2, abs=16, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=6, asz=2, abs=20, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=5, asz=2, abs=20, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=4, asz=2, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=3, asz=2, abs=24, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=2, asz=2, abs=28, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=28, disp=0 } .L8820: { loop_mode qppermb,1,sm %b[34], %b[33], %r36, %b[32] stqp,2 %r0, %b[4], %b[44] qppermb,4,sm %b[32], %b[31], %r36, %b[31] stqp,5 %r6, %b[4], %b[43] } { loop_mode qppermb,1,sm %b[42], %b[41], %r37, %b[32] stqp,2 %r2, %b[4], %b[32] qppermb,4,sm %b[40], %b[39], %r37, %b[31] stqp,5 %r1, %b[4], %b[31] } { loop_mode qppermb,1,sm %b[42], %b[41], %r36, %b[32] stqp,2 %r9, %b[4], %b[32] qppermb,4,sm %b[40], %b[39], %r36, %b[31] stqp,5 %r11, %b[4], %b[31] } { loop_mode qppermb,1,sm %b[38], %b[37], %r36, %b[32] stqp,2 %r7, %b[4], %b[32] qppermb,4,sm %b[38], %b[37], %r37, %b[31] stqp,5 %r10, %b[4], %b[31] } { loop_mode qppermb,1,sm %b[36], %b[35], %r36, %b[32] stqp,2 %r12, %b[4], %b[32] qppermb,4,sm %b[36], %b[35], %r37, %b[31] stqp,5 %r13, %b[4], %b[31] } { loop_mode qppermb,1,sm %b[30], %b[29], %r36, %b[30] stqp,2 %r14, %b[4], %b[32] qppermb,4,sm %b[30], %b[29], %r37, %b[29] stqp,5 %r15, %b[4], %b[31] movaqp,0 area=7, ind=0, am=1, be=0, %b[5] movaqp,1 area=7, ind=16, am=0, be=0, %b[6] movaqp,2 area=7, ind=0, am=1, be=0, %b[0] movaqp,3 area=7, ind=16, am=0, be=0, %b[1] } { loop_mode qppermb,1,sm %b[26], %b[25], %r36, %b[26] stqp,2 %r16, %b[4], %b[30] qppermb,4,sm %b[26], %b[25], %r37, %b[25] stqp,5 %r17, %b[4], %b[29] movaqp,0 area=6, ind=0, am=1, be=0, %b[13] movaqp,1 area=6, ind=16, am=0, be=0, %b[14] movaqp,2 area=6, ind=0, am=1, be=0, %b[9] movaqp,3 area=6, ind=16, am=0, be=0, %b[10] } { loop_mode qppermb,1,sm %b[28], %b[27], %r36, %b[26] stqp,2 %r18, %b[4], %b[26] qppermb,4,sm %b[28], %b[27], %r37, %b[25] stqp,5 %r19, %b[4], %b[25] movaqp,0 area=5, ind=0, am=1, be=0, %b[21] movaqp,1 area=5, ind=16, am=0, be=0, %b[22] movaqp,2 area=5, ind=0, am=1, be=0, %b[17] movaqp,3 area=5, ind=16, am=0, be=0, %b[18] } { loop_mode qppermb,1,sm %g16, %g17, %r36, %b[28] stqp,2 %r20, %b[4], %b[26] qppermb,4,sm %g16, %g17, %r37, %b[27] stqp,5 %r21, %b[4], %b[25] movaqp,0 area=4, ind=0, am=1, be=0, %b[25] movaqp,1 area=4, ind=16, am=0, be=0, %b[26] movaqp,2 area=4, ind=0, am=1, be=0, %g17 movaqp,3 area=4, ind=16, am=0, be=0, %g16 } { loop_mode qppermb,1,sm %b[24], %b[23], %r36, %b[30] stqp,2 %r22, %b[4], %b[28] qppermb,4,sm %b[24], %b[23], %r37, %b[29] stqp,5 %r23, %b[4], %b[27] movaqp,0 area=3, ind=0, am=1, be=0, %b[27] movaqp,1 area=3, ind=16, am=0, be=0, %b[28] movaqp,2 area=3, ind=0, am=1, be=0, %b[23] movaqp,3 area=3, ind=16, am=0, be=0, %b[24] } { loop_mode qppermb,1,sm %b[20], %b[19], %r36, %b[34] stqp,2 %r24, %b[4], %b[30] qppermb,4,sm %b[20], %b[19], %r37, %b[33] stqp,5 %r25, %b[4], %b[29] movaqp,0 area=0, ind=0, am=1, be=0, %b[31] movaqp,1 area=0, ind=16, am=0, be=0, %b[32] movaqp,2 area=0, ind=0, am=1, be=0, %b[29] movaqp,3 area=0, ind=16, am=0, be=0, %b[30] } { loop_mode qppermb,1,sm %b[16], %b[15], %r36, %b[38] stqp,2 %r26, %b[4], %b[34] qppermb,4,sm %b[16], %b[15], %r37, %b[37] stqp,5 %r27, %b[4], %b[33] movaqp,0 area=2, ind=0, am=1, be=0, %b[35] movaqp,1 area=2, ind=16, am=0, be=0, %b[36] movaqp,2 area=2, ind=0, am=1, be=0, %b[33] movaqp,3 area=2, ind=16, am=0, be=0, %b[34] } { loop_mode addd,0,sm %r5, 0x1, %r5 qppermb,1,sm %b[12], %b[11], %r36, %b[43] stqp,2 %r28, %b[4], %b[38] bitrevd,3,sm %r5, %b[41] qppermb,4,sm %b[12], %b[11], %r37, %b[42] stqp,5 %r29, %b[4], %b[37] movaqp,0 area=1, ind=0, am=1, be=0, %b[39] movaqp,1 area=1, ind=16, am=0, be=0, %b[40] movaqp,2 area=1, ind=0, am=1, be=0, %b[37] movaqp,3 area=1, ind=16, am=0, be=0, %b[38] } { loop_mode qppermb,1,sm %b[8], %b[7], %r36, %b[43] stqp,2 %r30, %b[4], %b[43] shrd,3,sm %b[41], %r38, %b[41] qppermb,4,sm %b[8], %b[7], %r37, %b[42] stqp,5 %r31, %b[4], %b[42] } { loop_mode qppermb,1,sm %b[3], %b[2], %r36, %b[42] stqp,2 %r32, %b[4], %b[43] shld,3,sm %b[41], 0x5, %b[2] qppermb,4,sm %b[3], %b[2], %r37, %b[41] stqp,5 %r33, %b[4], %b[42] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[32], %b[31], %r37, %b[42] stqp,2 %r34, %b[4], %b[42] qppermb,3,sm %b[30], %b[29], %r37, %b[41] stqp,5 %r35, %b[4], %b[41] }
Теоретическая скорость: 64 комплексных числа за 16 тактов (64/16) = 32 Байт/такт
Замеры скорости

Видим замедление по всей длине графика.
8. reverse_radix2_vector4_stream32
Чтение в 32 потока из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix2_vector4_stream32(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_00000 = (__v2di*)&data_in[ 0 * count_in/32]; __v2di *in_00001 = (__v2di*)&data_in[ 1 * count_in/32]; __v2di *in_00010 = (__v2di*)&data_in[ 2 * count_in/32]; __v2di *in_00011 = (__v2di*)&data_in[ 3 * count_in/32]; __v2di *in_00100 = (__v2di*)&data_in[ 4 * count_in/32]; __v2di *in_00101 = (__v2di*)&data_in[ 5 * count_in/32]; __v2di *in_00110 = (__v2di*)&data_in[ 6 * count_in/32]; __v2di *in_00111 = (__v2di*)&data_in[ 7 * count_in/32]; __v2di *in_01000 = (__v2di*)&data_in[ 8 * count_in/32]; __v2di *in_01001 = (__v2di*)&data_in[ 9 * count_in/32]; __v2di *in_01010 = (__v2di*)&data_in[10 * count_in/32]; __v2di *in_01011 = (__v2di*)&data_in[11 * count_in/32]; __v2di *in_01100 = (__v2di*)&data_in[12 * count_in/32]; __v2di *in_01101 = (__v2di*)&data_in[13 * count_in/32]; __v2di *in_01110 = (__v2di*)&data_in[14 * count_in/32]; __v2di *in_01111 = (__v2di*)&data_in[15 * count_in/32]; __v2di *in_10000 = (__v2di*)&data_in[16 * count_in/32]; __v2di *in_10001 = (__v2di*)&data_in[17 * count_in/32]; __v2di *in_10010 = (__v2di*)&data_in[18 * count_in/32]; __v2di *in_10011 = (__v2di*)&data_in[19 * count_in/32]; __v2di *in_10100 = (__v2di*)&data_in[20 * count_in/32]; __v2di *in_10101 = (__v2di*)&data_in[21 * count_in/32]; __v2di *in_10110 = (__v2di*)&data_in[22 * count_in/32]; __v2di *in_10111 = (__v2di*)&data_in[23 * count_in/32]; __v2di *in_11000 = (__v2di*)&data_in[24 * count_in/32]; __v2di *in_11001 = (__v2di*)&data_in[25 * count_in/32]; __v2di *in_11010 = (__v2di*)&data_in[26 * count_in/32]; __v2di *in_11011 = (__v2di*)&data_in[27 * count_in/32]; __v2di *in_11100 = (__v2di*)&data_in[28 * count_in/32]; __v2di *in_11101 = (__v2di*)&data_in[29 * count_in/32]; __v2di *in_11110 = (__v2di*)&data_in[30 * count_in/32]; __v2di *in_11111 = (__v2di*)&data_in[31 * count_in/32]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/32; ++i) { int64_t offset = 32 * (__builtin_e2k_bitrevd(i) >> shift_out); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_00000[2*i+1], in_00000[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_00000[2*i+1], in_00000[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_10000[2*i+1], in_10000[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_10000[2*i+1], in_10000[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_01000[2*i+1], in_01000[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_01000[2*i+1], in_01000[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_11000[2*i+1], in_11000[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_11000[2*i+1], in_11000[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 8*16) = __builtin_e2k_qppermb(in_00100[2*i+1], in_00100[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 9*16) = __builtin_e2k_qppermb(in_00100[2*i+1], in_00100[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 10*16) = __builtin_e2k_qppermb(in_10100[2*i+1], in_10100[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 11*16) = __builtin_e2k_qppermb(in_10100[2*i+1], in_10100[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 12*16) = __builtin_e2k_qppermb(in_01100[2*i+1], in_01100[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 13*16) = __builtin_e2k_qppermb(in_01100[2*i+1], in_01100[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 14*16) = __builtin_e2k_qppermb(in_11100[2*i+1], in_11100[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 15*16) = __builtin_e2k_qppermb(in_11100[2*i+1], in_11100[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 16*16) = __builtin_e2k_qppermb(in_00010[2*i+1], in_00010[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 17*16) = __builtin_e2k_qppermb(in_00010[2*i+1], in_00010[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 18*16) = __builtin_e2k_qppermb(in_10010[2*i+1], in_10010[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 19*16) = __builtin_e2k_qppermb(in_10010[2*i+1], in_10010[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 20*16) = __builtin_e2k_qppermb(in_01010[2*i+1], in_01010[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 21*16) = __builtin_e2k_qppermb(in_01010[2*i+1], in_01010[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 22*16) = __builtin_e2k_qppermb(in_11010[2*i+1], in_11010[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 23*16) = __builtin_e2k_qppermb(in_11010[2*i+1], in_11010[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 24*16) = __builtin_e2k_qppermb(in_00110[2*i+1], in_00110[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 25*16) = __builtin_e2k_qppermb(in_00110[2*i+1], in_00110[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 26*16) = __builtin_e2k_qppermb(in_10110[2*i+1], in_10110[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 27*16) = __builtin_e2k_qppermb(in_10110[2*i+1], in_10110[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 28*16) = __builtin_e2k_qppermb(in_01110[2*i+1], in_01110[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 29*16) = __builtin_e2k_qppermb(in_01110[2*i+1], in_01110[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 30*16) = __builtin_e2k_qppermb(in_11110[2*i+1], in_11110[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 31*16) = __builtin_e2k_qppermb(in_11110[2*i+1], in_11110[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 32*16) = __builtin_e2k_qppermb(in_00001[2*i+1], in_00001[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 33*16) = __builtin_e2k_qppermb(in_00001[2*i+1], in_00001[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 34*16) = __builtin_e2k_qppermb(in_10001[2*i+1], in_10001[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 35*16) = __builtin_e2k_qppermb(in_10001[2*i+1], in_10001[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 36*16) = __builtin_e2k_qppermb(in_01001[2*i+1], in_01001[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 37*16) = __builtin_e2k_qppermb(in_01001[2*i+1], in_01001[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 38*16) = __builtin_e2k_qppermb(in_11001[2*i+1], in_11001[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 39*16) = __builtin_e2k_qppermb(in_11001[2*i+1], in_11001[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 40*16) = __builtin_e2k_qppermb(in_00101[2*i+1], in_00101[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 41*16) = __builtin_e2k_qppermb(in_00101[2*i+1], in_00101[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 42*16) = __builtin_e2k_qppermb(in_10101[2*i+1], in_10101[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 43*16) = __builtin_e2k_qppermb(in_10101[2*i+1], in_10101[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 44*16) = __builtin_e2k_qppermb(in_01101[2*i+1], in_01101[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 45*16) = __builtin_e2k_qppermb(in_01101[2*i+1], in_01101[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 46*16) = __builtin_e2k_qppermb(in_11101[2*i+1], in_11101[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 47*16) = __builtin_e2k_qppermb(in_11101[2*i+1], in_11101[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 48*16) = __builtin_e2k_qppermb(in_00011[2*i+1], in_00011[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 49*16) = __builtin_e2k_qppermb(in_00011[2*i+1], in_00011[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 50*16) = __builtin_e2k_qppermb(in_10011[2*i+1], in_10011[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 51*16) = __builtin_e2k_qppermb(in_10011[2*i+1], in_10011[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 52*16) = __builtin_e2k_qppermb(in_01011[2*i+1], in_01011[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 53*16) = __builtin_e2k_qppermb(in_01011[2*i+1], in_01011[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 54*16) = __builtin_e2k_qppermb(in_11011[2*i+1], in_11011[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 55*16) = __builtin_e2k_qppermb(in_11011[2*i+1], in_11011[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 56*16) = __builtin_e2k_qppermb(in_00111[2*i+1], in_00111[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 57*16) = __builtin_e2k_qppermb(in_00111[2*i+1], in_00111[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 58*16) = __builtin_e2k_qppermb(in_10111[2*i+1], in_10111[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 59*16) = __builtin_e2k_qppermb(in_10111[2*i+1], in_10111[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 60*16) = __builtin_e2k_qppermb(in_01111[2*i+1], in_01111[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 61*16) = __builtin_e2k_qppermb(in_01111[2*i+1], in_01111[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 62*16) = __builtin_e2k_qppermb(in_11111[2*i+1], in_11111[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 63*16) = __builtin_e2k_qppermb(in_11111[2*i+1], in_11111[2*i+0], mask_imag); } }
Основной цикл на ассемблере
.L13622: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=17, incr=1, ind=0, asz=1, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=16, incr=1, ind=0, asz=1, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=15, incr=1, ind=0, asz=1, abs=2, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=14, incr=1, ind=0, asz=1, abs=2, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=13, incr=1, ind=0, asz=1, abs=4, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=12, incr=1, ind=0, asz=1, abs=4, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=11, incr=1, ind=0, asz=1, abs=6, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=10, incr=1, ind=0, asz=1, abs=6, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=9, incr=1, ind=0, asz=1, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=8, incr=1, ind=0, asz=1, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=7, incr=1, ind=0, asz=1, abs=10, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=6, incr=1, ind=0, asz=1, abs=10, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=5, incr=1, ind=0, asz=1, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=4, incr=1, ind=0, asz=1, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=3, incr=1, ind=0, asz=1, abs=14, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=2, incr=1, ind=0, asz=1, abs=14, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=1, incr=1, ind=0, asz=1, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=15, asz=1, abs=16, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=14, asz=1, abs=18, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=13, asz=1, abs=18, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=12, asz=1, abs=20, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=11, asz=1, abs=20, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=10, asz=1, abs=22, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=9, asz=1, abs=22, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=8, asz=1, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=7, asz=1, abs=24, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=6, asz=1, abs=26, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=5, asz=1, abs=26, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=4, asz=1, abs=28, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=3, asz=1, abs=28, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=2, asz=1, abs=30, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=30, disp=0 } .L13394: { loop_mode disp %ctpr1, .L13394 bitrevd,0 %r1, %g20 addd,1 %r1, 0x1, %r1 movaqp,0 area=0, ind=16, am=1, be=0, %g17 movaqp,1 area=0, ind=0, am=0, be=0, %g16 movaqp,2 area=0, ind=16, am=1, be=0, %g19 movaqp,3 area=0, ind=0, am=0, be=0, %g18 } { loop_mode disp %ctpr3, .L12302 shrd,0 %g20, %r6, %g20 movaqp,0 area=1, ind=16, am=1, be=0, %g22 movaqp,1 area=1, ind=0, am=0, be=0, %g21 movaqp,2 area=1, ind=16, am=1, be=0, %g24 movaqp,3 area=1, ind=0, am=0, be=0, %g23 } { loop_mode shld,0 %g20, 0x5, %g20 movaqp,0 area=2, ind=16, am=1, be=0, %g26 movaqp,1 area=2, ind=0, am=0, be=0, %g25 movaqp,2 area=2, ind=16, am=1, be=0, %g28 movaqp,3 area=2, ind=0, am=0, be=0, %g27 } { loop_mode addd,0 %r2, %g20, %r7 movaqp,0 area=3, ind=16, am=1, be=0, %g30 movaqp,1 area=3, ind=0, am=0, be=0, %g29 movaqp,2 area=3, ind=16, am=1, be=0, %r4 movaqp,3 area=3, ind=0, am=0, be=0, %g31 } { loop_mode movaqp,0 area=4, ind=16, am=1, be=0, %r10 movaqp,1 area=4, ind=0, am=0, be=0, %r9 movaqp,2 area=4, ind=16, am=1, be=0, %r12 movaqp,3 area=4, ind=0, am=0, be=0, %r11 } { loop_mode qppermb,0 %g17, %g16, %r0, %r17 qppermb,1 %g17, %g16, %r5, %g16 qppermb,3 %g19, %g18, %r0, %g17 qppermb,4 %g19, %g18, %r5, %g18 movaqp,0 area=5, ind=16, am=1, be=0, %r14 movaqp,1 area=5, ind=0, am=0, be=0, %r13 movaqp,2 area=5, ind=16, am=1, be=0, %r16 movaqp,3 area=5, ind=0, am=0, be=0, %r15 } { loop_mode qppermb,0 %g22, %g21, %r0, %r21 qppermb,1 %g22, %g21, %r5, %g21 stqp,2 0x10, %r7, %g16 qppermb,3 %g24, %g23, %r0, %g22 qppermb,4 %g24, %g23, %r5, %g23 stqp,5 %r7, _f16s,_lts0lo 0x30, %g18 movaqp,0 area=6, ind=16, am=1, be=0, %r18 movaqp,1 area=6, ind=0, am=0, be=0, %g19 movaqp,2 area=6, ind=16, am=1, be=0, %r20 movaqp,3 area=6, ind=0, am=0, be=0, %r19 } { loop_mode qppermb,0 %g26, %g25, %r0, %g17 qppermb,1 %g26, %g25, %r5, %g20 stqp,2 %r2, %g20, %r17 qppermb,3 %g28, %g27, %r0, %g25 qppermb,4 %g28, %g27, %r5, %g26 stqp,5 %r7, _f16s,_lts0lo 0x20, %g17 movaqp,0 area=7, ind=16, am=1, be=0, %g18 movaqp,1 area=7, ind=0, am=0, be=0, %g16 movaqp,2 area=7, ind=16, am=1, be=0, %r22 movaqp,3 area=7, ind=0, am=0, be=0, %g24 } { loop_mode qppermb,0 %g30, %g29, %r0, %r24 qppermb,1 %g30, %g29, %r5, %g29 stqp,2 %r7, _f16s,_lts0lo 0x50, %g21 qppermb,3 %r4, %g31, %r0, %g21 qppermb,4 %r4, %g31, %r5, %g23 stqp,5 %r7, _f16s,_lts0hi 0x70, %g23 movaqp,0 area=8, ind=16, am=1, be=0, %g28 movaqp,1 area=8, ind=0, am=0, be=0, %g27 movaqp,2 area=8, ind=16, am=1, be=0, %r23 movaqp,3 area=8, ind=0, am=0, be=0, %r17 } { loop_mode qppermb,0 %r10, %r9, %r0, %g22 qppermb,1 %r10, %r9, %r5, %r9 stqp,2 %r7, _f16s,_lts0lo 0x40, %r21 qppermb,3 %r12, %r11, %r0, %r10 qppermb,4 %r12, %r11, %r5, %r11 stqp,5 %r7, _f16s,_lts0hi 0x60, %g22 movaqp,0 area=9, ind=16, am=1, be=0, %g31 movaqp,1 area=9, ind=0, am=0, be=0, %g30 movaqp,2 area=9, ind=16, am=1, be=0, %r25 movaqp,3 area=9, ind=0, am=0, be=0, %r4 } { loop_mode qppermb,0 %r14, %r13, %r0, %g17 qppermb,1 %r14, %r13, %r5, %g20 stqp,2 %r7, _f16s,_lts0lo 0x80, %g17 qppermb,3 %r16, %r15, %r0, %r13 qppermb,4 %r16, %r15, %r5, %r14 stqp,5 %r7, _f16s,_lts0hi 0x90, %g20 movaqp,0 area=10, ind=16, am=1, be=0, %r21 movaqp,1 area=10, ind=0, am=0, be=0, %r12 movaqp,2 area=10, ind=16, am=1, be=0, %r27 movaqp,3 area=10, ind=0, am=0, be=0, %r26 } { loop_mode qppermb,0 %r18, %g19, %r0, %r28 qppermb,1 %r18, %g19, %r5, %g19 stqp,2 %r7, _f16s,_lts0lo 0xa0, %g25 qppermb,3 %r20, %r19, %r0, %r18 qppermb,4 %r20, %r19, %r5, %r19 stqp,5 %r7, _f16s,_lts0hi 0xb0, %g26 movaqp,0 area=11, ind=16, am=1, be=0, %r16 movaqp,1 area=11, ind=0, am=0, be=0, %r15 movaqp,2 area=11, ind=16, am=1, be=0, %g26 movaqp,3 area=11, ind=0, am=0, be=0, %g25 } { loop_mode qppermb,0 %g18, %g16, %r0, %r30 qppermb,1 %g18, %g16, %r5, %g16 stqp,2 %r7, _f16s,_lts0lo 0xc0, %r24 qppermb,3 %r22, %g24, %r0, %g18 qppermb,4 %r22, %g24, %r5, %g24 stqp,5 %r7, _f16s,_lts0hi 0xd0, %g29 movaqp,0 area=12, ind=16, am=1, be=0, %r20 movaqp,1 area=12, ind=0, am=0, be=0, %g29 movaqp,2 area=12, ind=16, am=1, be=0, %r29 movaqp,3 area=12, ind=0, am=0, be=0, %r24 } { loop_mode qppermb,0 %g28, %g27, %r0, %r32 qppermb,1 %g28, %g27, %r5, %g27 stqp,2 %r7, _f16s,_lts0lo 0xe0, %g21 qppermb,3 %r23, %r17, %r0, %g28 qppermb,4 %r23, %r17, %r5, %r17 stqp,5 %r7, _f16s,_lts0hi 0xf0, %g23 movaqp,0 area=13, ind=16, am=1, be=0, %g23 movaqp,1 area=13, ind=0, am=0, be=0, %g21 movaqp,2 area=13, ind=16, am=1, be=0, %r31 movaqp,3 area=13, ind=0, am=0, be=0, %r22 } { loop_mode qppermb,0 %g31, %g30, %r0, %r34 qppermb,1 %g31, %g30, %r5, %g30 stqp,2 %r7, _f16s,_lts0lo 0x100, %g22 qppermb,3 %r25, %r4, %r0, %g31 qppermb,4 %r25, %r4, %r5, %r4 stqp,5 %r7, _f16s,_lts0hi 0x110, %r9 movaqp,0 area=14, ind=16, am=1, be=0, %r9 movaqp,1 area=14, ind=0, am=0, be=0, %g22 movaqp,2 area=14, ind=16, am=1, be=0, %r33 movaqp,3 area=14, ind=0, am=0, be=0, %r23 } { loop_mode qppermb,0 %r21, %r12, %r0, %r36 qppermb,1 %r21, %r12, %r5, %r12 stqp,2 %r7, _f16s,_lts0lo 0x120, %r10 qppermb,3 %r27, %r26, %r0, %r21 qppermb,4 %r27, %r26, %r5, %r26 stqp,5 %r7, _f16s,_lts0hi 0x130, %r11 movaqp,0 area=15, ind=16, am=1, be=0, %r11 movaqp,1 area=15, ind=0, am=0, be=0, %r10 movaqp,2 area=15, ind=16, am=1, be=0, %r35 movaqp,3 area=15, ind=0, am=0, be=0, %r25 } { loop_mode qppermb,0 %r16, %r15, %r0, %g17 qppermb,1 %r16, %r15, %r5, %g20 stqp,2 %r7, _f16s,_lts0lo 0x140, %g17 qppermb,3 %g26, %g25, %r0, %r15 qppermb,4 %g26, %g25, %r5, %g25 stqp,5 %r7, _f16s,_lts0hi 0x150, %g20 } { loop_mode qppermb,0 %r20, %g29, %r0, %g26 qppermb,1 %r20, %g29, %r5, %g29 stqp,2 %r7, _f16s,_lts0lo 0x160, %r13 qppermb,3 %r29, %r24, %r0, %r13 qppermb,4 %r29, %r24, %r5, %r14 stqp,5 %r7, _f16s,_lts0hi 0x170, %r14 } { loop_mode qppermb,0 %g23, %g21, %r0, %g19 qppermb,1 %g23, %g21, %r5, %g21 stqp,2 %r7, _f16s,_lts0lo 0x180, %r28 qppermb,3 %r31, %r22, %r0, %g23 qppermb,4 %r31, %r22, %r5, %r16 stqp,5 %r7, _f16s,_lts0hi 0x190, %g19 } { loop_mode qppermb,0 %r9, %g22, %r0, %r18 qppermb,1 %r9, %g22, %r5, %g22 stqp,2 %r7, _f16s,_lts0lo 0x1a0, %r18 qppermb,3 %r33, %r23, %r0, %r9 qppermb,4 %r33, %r23, %r5, %r19 stqp,5 %r7, _f16s,_lts0hi 0x1b0, %r19 } { loop_mode qppermb,0 %r11, %r10, %r0, %g16 qppermb,1 %r11, %r10, %r5, %r10 stqp,2 %r7, _f16s,_lts0lo 0x1c0, %r30 qppermb,3 %r35, %r25, %r0, %r11 qppermb,4 %r35, %r25, %r5, %r20 stqp,5 %r7, _f16s,_lts0hi 0x1d0, %g16 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x1e0, %g18 stqp,5 %r7, _f16s,_lts0hi 0x1f0, %g24 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x200, %r32 stqp,5 %r7, _f16s,_lts0hi 0x210, %g27 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x220, %g28 stqp,5 %r7, _f16s,_lts0hi 0x230, %r17 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x240, %r34 stqp,5 %r7, _f16s,_lts0hi 0x250, %g30 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x260, %g31 stqp,5 %r7, _f16s,_lts0hi 0x270, %r4 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x280, %r36 stqp,5 %r7, _f16s,_lts0hi 0x290, %r12 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x2a0, %r21 stqp,5 %r7, _f16s,_lts0hi 0x2b0, %r26 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x2c0, %g17 stqp,5 %r7, _f16s,_lts0hi 0x2d0, %g20 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x2e0, %r15 stqp,5 %r7, _f16s,_lts0hi 0x2f0, %g25 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x300, %g26 stqp,5 %r7, _f16s,_lts0hi 0x310, %g29 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x320, %r13 stqp,5 %r7, _f16s,_lts0hi 0x330, %r14 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x340, %g19 stqp,5 %r7, _f16s,_lts0hi 0x350, %g21 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x360, %g23 stqp,5 %r7, _f16s,_lts0hi 0x370, %r16 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x380, %r18 stqp,5 %r7, _f16s,_lts0hi 0x390, %g22 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x3a0, %r9 stqp,5 %r7, _f16s,_lts0hi 0x3b0, %r19 } { loop_mode stqp,2 %r7, _f16s,_lts0lo 0x3c0, %g16 stqp,5 %r7, _f16s,_lts0hi 0x3d0, %r10 } { loop_mode ct %ctpr1 ? %NOT_LOOP_END alc alcf=0, alct=1 stqp,2 %r7, _f16s,_lts0lo 0x3e0, %r11 stqp,5 %r7, _f16s,_lts0hi 0x3f0, %r20 } { loop_mode ct %ctpr3 alc alcf=0, alct=1 }
Теоретическая скорость: 128 комплексных чисел за 39 тактов (128/39) = 26.26 Байт/такт
Замеры скорости

Видим замедление по всей длине графика.
При попытке чтения в 64 потока получается резко менее эффективный код.
APB не может читать в 64 потока, поэтому в дополнение к APB компилятор вставляет обычные операции чтения ldqp. В итоге скорость резко проседает.
Итоги по reverse_radix2_vector4


Победителем можно считать reverse_radix2_vector4_stream8.
При реализации Vector-4 Radix-2 FFT будем использовать его.
reverse_radix4_vector2
1. reverse_radix4_vector2_etalon
Эталонный вариант для сравнения на корректность.
Здесь reverseNumber_radix4 вычисляется с помощью цикла.
int reverseNumber_radix4(int number, int bit_count) { int answer = 0; for(int i = 0; i < bit_count/2; ++i) { answer <<= 2; answer |= number & 3; number >>= 2; } return answer; } void reverse_radix4_vector2_etalon(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; for(int64_t i = 0; i < count_out; ++i) { int index = reverseNumber_radix4(i, bit_count_out); myComplex a = data_in[2*i + 0]; myComplex b = data_in[2*i + 1]; data_out[index] = (myComplex2){.real = {a.real, b.real}, .imag = {a.imag, b.imag}}; } }
2. reverse_radix4_vector2
В процессоре нет готовых инструкций, производящих операцию reverseNumber_radix4.
Поэтому выполним инструкцию bitrevd и переставим соседние биты местами.
Код на Си
void reverse_radix4_vector2(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in = (__v2di*)data_in; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out; ++i) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 16 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)data_out + offset) = __builtin_e2k_qppermb(in[i], in[i], mask); } }
Основной цикл на ассемблере
.L4546: { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=1, asz=5, abs=0, disp=0 } .L4397: { loop_mode bitrevd,0,sm %b[10], %b[1] shr_andd,1,sm %b[3], %r5, %r6, %b[9] ord,2,sm %b[11], %b[7], %b[12] shld,3,sm %b[14], 0x4, %b[15] qppermb,4,sm %b[6], %b[6], %r0, %b[13] addd,5,sm %b[10], 0x1, %b[8] movaqp,1 area=0, ind=0, am=1, be=0, %b[0] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END shr_andd,1,sm %b[1], %r4, %r7, %b[3] stqp,5 %r2, %b[15], %b[13] }
Теоретическая скорость: 2 комплексных числа за 2 такта (2/2) = 8 Байт/такт
Замеры скорости

3. reverse_radix4_vector2_x4
Сделаем ручную раскрутку цикла в 4 раза.
Код на Си
void reverse_radix4_vector2_x4(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in = (__v2di*) data_in; __v2di *out_0 = (__v2di*)&data_out[0 * count_out/4]; __v2di *out_1 = (__v2di*)&data_out[1 * count_out/4]; __v2di *out_2 = (__v2di*)&data_out[2 * count_out/4]; __v2di *out_3 = (__v2di*)&data_out[3 * count_out/4]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out; i += 4) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 16 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)out_0 + offset) = __builtin_e2k_qppermb(in[i + 0], in[i + 0], mask); *(__v2du*)((void*)out_1 + offset) = __builtin_e2k_qppermb(in[i + 1], in[i + 1], mask); *(__v2du*)((void*)out_2 + offset) = __builtin_e2k_qppermb(in[i + 2], in[i + 2], mask); *(__v2du*)((void*)out_3 + offset) = __builtin_e2k_qppermb(in[i + 3], in[i + 3], mask); } }
Основной цикл на ассемблере
.L4888: { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=32 } .L4593: { loop_mode qppermb,0,sm %b[11], %b[11], %r7, %b[20] shr_andd,1,sm %b[26], %r9, %r11, %b[16] qppermb,3,sm %b[12], %b[12], %r7, %b[23] shr_andd,4,sm %b[26], %r6, %r10, %b[19] addd,5,sm %b[17], 0x4, %b[15] movaqp,0 area=0, ind=16, am=1, be=0, %b[7] movaqp,1 area=0, ind=0, am=0, be=0, %b[8] movaqp,2 area=0, ind=16, am=1, be=0, %b[0] movaqp,3 area=0, ind=0, am=0, be=0, %b[1] } { loop_mode bitrevd,0,sm %b[17], %b[24] qppermb,1,sm %b[4], %b[4], %r7, %b[11] stqp,2 %r5, %b[6], %b[22] ord,3,sm %b[21], %b[18], %b[27] qppermb,4,sm %b[5], %b[5], %r7, %b[12] stqp,5 %r2, %b[6], %b[25] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END stqp,2 %r0, %b[6], %b[13] shld,4,sm %b[27], 0x4, %b[4] stqp,5 %r4, %b[6], %b[14] }
Теоретическая скорость: 8 комплексных чисел за 3 такта (8/3) = 21.33 Байт/такт
Замеры скорости

Видим ускорение в начале графика.
Здесь происходит четыре чтения из одного места памяти и запись в четыре разных места памяти.
4. reverse_radix4_vector2_stream4
Теперь сделаем наоборот: будем читать из четырёх разных мест, а писать рядом.
Чтение в 4 потока из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix4_vector2_stream4(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_0 = (__v2di*)&data_in[0 * count_in/4]; __v2di *in_1 = (__v2di*)&data_in[1 * count_in/4]; __v2di *in_2 = (__v2di*)&data_in[2 * count_in/4]; __v2di *in_3 = (__v2di*)&data_in[3 * count_in/4]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/4; ++i) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 16 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_0[i], in_0[i], mask); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_1[i], in_1[i], mask); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_2[i], in_2[i], mask); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_3[i], in_3[i], mask); } }
Основной цикл на ассемблере
.L5211: { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=4, asz=4, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=3, asz=4, abs=0, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=2, asz=4, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=1, asz=4, abs=16, disp=0 } .L4931: { loop_mode qppermb,0,sm %b[11], %b[11], %r7, %b[20] shr_andd,1,sm %b[26], %r9, %r11, %b[16] qppermb,3,sm %b[12], %b[12], %r7, %b[23] shr_andd,4,sm %b[26], %r6, %r10, %b[19] addd,5,sm %b[17], 0x1, %b[15] movaqp,0 area=1, ind=0, am=1, be=0, %b[1] movaqp,1 area=0, ind=0, am=1, be=0, %b[8] movaqp,2 area=1, ind=0, am=1, be=0, %b[0] movaqp,3 area=0, ind=0, am=1, be=0, %b[7] } { loop_mode bitrevd,0,sm %b[17], %b[24] qppermb,1,sm %b[4], %b[4], %r7, %b[11] stqp,2 %r0, %b[6], %b[22] ord,3,sm %b[21], %b[18], %b[27] qppermb,4,sm %b[5], %b[5], %r7, %b[12] stqp,5 %r2, %b[6], %b[25] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END stqp,2 %r5, %b[6], %b[13] shld,4,sm %b[27], 0x4, %b[4] stqp,5 %r4, %b[6], %b[14] }
Теоретическая скорость: 8 комплексных чисел за 3 такта (8/3) = 21.33 Байт/такт
Замеры скорости

Видим желаемое ускорение по всей длине графика.
5. reverse_radix4_vector2_stream16
Чтение в 16 потоков из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix4_vector2_stream16(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_00 = (__v2di*)&data_in[ 0 * count_in/16]; __v2di *in_01 = (__v2di*)&data_in[ 1 * count_in/16]; __v2di *in_02 = (__v2di*)&data_in[ 2 * count_in/16]; __v2di *in_03 = (__v2di*)&data_in[ 3 * count_in/16]; __v2di *in_10 = (__v2di*)&data_in[ 4 * count_in/16]; __v2di *in_11 = (__v2di*)&data_in[ 5 * count_in/16]; __v2di *in_12 = (__v2di*)&data_in[ 6 * count_in/16]; __v2di *in_13 = (__v2di*)&data_in[ 7 * count_in/16]; __v2di *in_20 = (__v2di*)&data_in[ 8 * count_in/16]; __v2di *in_21 = (__v2di*)&data_in[ 9 * count_in/16]; __v2di *in_22 = (__v2di*)&data_in[10 * count_in/16]; __v2di *in_23 = (__v2di*)&data_in[11 * count_in/16]; __v2di *in_30 = (__v2di*)&data_in[12 * count_in/16]; __v2di *in_31 = (__v2di*)&data_in[13 * count_in/16]; __v2di *in_32 = (__v2di*)&data_in[14 * count_in/16]; __v2di *in_33 = (__v2di*)&data_in[15 * count_in/16]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/16; ++i) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 16 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_00[i], in_00[i], mask); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_10[i], in_10[i], mask); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_20[i], in_20[i], mask); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_30[i], in_30[i], mask); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_01[i], in_01[i], mask); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_11[i], in_11[i], mask); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_21[i], in_21[i], mask); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_31[i], in_31[i], mask); *(__v2du*)((void*)data_out + offset + 8*16) = __builtin_e2k_qppermb(in_02[i], in_02[i], mask); *(__v2du*)((void*)data_out + offset + 9*16) = __builtin_e2k_qppermb(in_12[i], in_12[i], mask); *(__v2du*)((void*)data_out + offset + 10*16) = __builtin_e2k_qppermb(in_22[i], in_22[i], mask); *(__v2du*)((void*)data_out + offset + 11*16) = __builtin_e2k_qppermb(in_32[i], in_32[i], mask); *(__v2du*)((void*)data_out + offset + 12*16) = __builtin_e2k_qppermb(in_03[i], in_03[i], mask); *(__v2du*)((void*)data_out + offset + 13*16) = __builtin_e2k_qppermb(in_13[i], in_13[i], mask); *(__v2du*)((void*)data_out + offset + 14*16) = __builtin_e2k_qppermb(in_23[i], in_23[i], mask); *(__v2du*)((void*)data_out + offset + 15*16) = __builtin_e2k_qppermb(in_33[i], in_33[i], mask); } }
Основной цикл на ассемблере
.L6141: { fapb ct=0, dcd=0, fmt=5, mrng=16, d=1, incr=0, ind=0, asz=2, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=15, asz=2, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=14, asz=2, abs=4, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=13, asz=2, abs=4, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=12, asz=2, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=11, asz=2, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=10, asz=2, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=9, asz=2, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=8, asz=2, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=7, asz=2, abs=16, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=6, asz=2, abs=20, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=5, asz=2, abs=20, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=4, asz=2, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=3, asz=2, abs=24, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=2, asz=2, abs=28, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=1, asz=2, abs=28, disp=0 } .L5350: { loop_mode qppermb,0,sm %b[12], %b[12], %r20, %b[46] shr_andd,1,sm %b[24], %r21, %r24, %b[1] stqp,2 %r0, %b[21], %b[4] qppermb,3,sm %b[17], %b[17], %r20, %b[47] shr_andd,4,sm %b[24], %r22, %r23, %b[5] stqp,5 %r2, %b[21], %b[16] } { loop_mode qppermb,1,sm %b[41], %b[41], %r20, %b[4] stqp,2 %r6, %b[21], %b[46] qppermb,4,sm %b[42], %b[42], %r20, %b[9] stqp,5 %r5, %b[21], %b[47] } { loop_mode qppermb,1,sm %b[40], %b[40], %r20, %b[24] stqp,2 %r10, %b[21], %b[6] qppermb,4,sm %b[39], %b[39], %r20, %b[23] stqp,5 %r7, %b[21], %b[11] movaqp,0 area=7, ind=0, am=1, be=0, %b[16] movaqp,1 area=6, ind=0, am=1, be=0, %b[20] movaqp,2 area=7, ind=0, am=1, be=0, %b[12] movaqp,3 area=6, ind=0, am=1, be=0, %b[17] } { loop_mode qppermb,1,sm %b[38], %b[38], %r20, %b[30] stqp,2 %r9, %b[21], %b[26] qppermb,4,sm %b[37], %b[37], %r20, %b[29] stqp,5 %r11, %b[21], %b[25] movaqp,0 area=5, ind=0, am=1, be=0, %b[11] movaqp,1 area=4, ind=0, am=1, be=0, %b[36] movaqp,2 area=5, ind=0, am=1, be=0, %b[6] movaqp,3 area=4, ind=0, am=1, be=0, %b[35] } { loop_mode qppermb,1,sm %b[13], %b[13], %r20, %b[26] stqp,2 %r12, %b[21], %b[32] qppermb,4,sm %b[8], %b[8], %r20, %b[25] stqp,5 %r13, %b[21], %b[31] movaqp,0 area=3, ind=0, am=1, be=0, %b[39] movaqp,1 area=2, ind=0, am=1, be=0, %b[40] movaqp,2 area=3, ind=0, am=1, be=0, %b[37] movaqp,3 area=2, ind=0, am=1, be=0, %b[38] } { loop_mode addd,0,sm %b[2], 0x1, %b[0] qppermb,1,sm %b[22], %b[22], %r20, %b[42] stqp,2 %r14, %b[21], %b[28] ord,3,sm %b[5], %b[1], %b[45] qppermb,4,sm %b[19], %b[19], %r20, %b[41] stqp,5 %r15, %b[21], %b[27] movaqp,0 area=1, ind=0, am=1, be=0, %b[13] movaqp,1 area=0, ind=0, am=1, be=0, %b[32] movaqp,2 area=1, ind=0, am=1, be=0, %b[8] movaqp,3 area=0, ind=0, am=1, be=0, %b[31] } { loop_mode bitrevd,0,sm %b[2], %b[22] qppermb,1,sm %b[18], %b[18], %r20, %b[5] stqp,2 %r16, %b[21], %b[44] shld,3,sm %b[45], 0x4, %b[19] qppermb,4,sm %b[14], %b[14], %r20, %b[1] stqp,5 %r17, %b[21], %b[43] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[33], %b[33], %r20, %b[2] stqp,2 %r18, %b[21], %b[7] qppermb,3,sm %b[34], %b[34], %r20, %b[14] stqp,5 %r19, %b[21], %b[3] }
Теоретическая скорость: 32 комплексных числа за 8 тактов (32/8) = 32 Байт/такт
Замеры скорости

Видим сильное ускорение.
При попытке чтения в 64 потока получается резко менее эффективный код.
APB не может читать в 64 потока, поэтому в дополнение к APB компилятор вставляет обычные операции чтения ldqp. В итоге скорость резко проседает.
Но можно сделать чтение в 32 потока. Для этого напишем чтение в 64 потока и обработаем сначала одну половину строк в одном цикле, а потом вторую половину строк во втором цикле. В каждом цикле будут использованы 32 потока чтения APB.
6. reverse_radix4_vector2_stream32
Чтение в 32 потока из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix4_vector2_stream32(int bit_count_in, myComplex *data_in, myComplex2 *data_out) { int bit_count_out = bit_count_in - 1; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_000 = (__v2di*)&data_in[ 0 * count_in/64]; __v2di *in_001 = (__v2di*)&data_in[ 1 * count_in/64]; __v2di *in_002 = (__v2di*)&data_in[ 2 * count_in/64]; __v2di *in_003 = (__v2di*)&data_in[ 3 * count_in/64]; __v2di *in_010 = (__v2di*)&data_in[ 4 * count_in/64]; __v2di *in_011 = (__v2di*)&data_in[ 5 * count_in/64]; __v2di *in_012 = (__v2di*)&data_in[ 6 * count_in/64]; __v2di *in_013 = (__v2di*)&data_in[ 7 * count_in/64]; __v2di *in_020 = (__v2di*)&data_in[ 8 * count_in/64]; __v2di *in_021 = (__v2di*)&data_in[ 9 * count_in/64]; __v2di *in_022 = (__v2di*)&data_in[10 * count_in/64]; __v2di *in_023 = (__v2di*)&data_in[11 * count_in/64]; __v2di *in_030 = (__v2di*)&data_in[12 * count_in/64]; __v2di *in_031 = (__v2di*)&data_in[13 * count_in/64]; __v2di *in_032 = (__v2di*)&data_in[14 * count_in/64]; __v2di *in_033 = (__v2di*)&data_in[15 * count_in/64]; __v2di *in_100 = (__v2di*)&data_in[16 * count_in/64]; __v2di *in_101 = (__v2di*)&data_in[17 * count_in/64]; __v2di *in_102 = (__v2di*)&data_in[18 * count_in/64]; __v2di *in_103 = (__v2di*)&data_in[19 * count_in/64]; __v2di *in_110 = (__v2di*)&data_in[20 * count_in/64]; __v2di *in_111 = (__v2di*)&data_in[21 * count_in/64]; __v2di *in_112 = (__v2di*)&data_in[22 * count_in/64]; __v2di *in_113 = (__v2di*)&data_in[23 * count_in/64]; __v2di *in_120 = (__v2di*)&data_in[24 * count_in/64]; __v2di *in_121 = (__v2di*)&data_in[25 * count_in/64]; __v2di *in_122 = (__v2di*)&data_in[26 * count_in/64]; __v2di *in_123 = (__v2di*)&data_in[27 * count_in/64]; __v2di *in_130 = (__v2di*)&data_in[28 * count_in/64]; __v2di *in_131 = (__v2di*)&data_in[29 * count_in/64]; __v2di *in_132 = (__v2di*)&data_in[30 * count_in/64]; __v2di *in_133 = (__v2di*)&data_in[31 * count_in/64]; __v2di *in_200 = (__v2di*)&data_in[32 * count_in/64]; __v2di *in_201 = (__v2di*)&data_in[33 * count_in/64]; __v2di *in_202 = (__v2di*)&data_in[34 * count_in/64]; __v2di *in_203 = (__v2di*)&data_in[35 * count_in/64]; __v2di *in_210 = (__v2di*)&data_in[36 * count_in/64]; __v2di *in_211 = (__v2di*)&data_in[37 * count_in/64]; __v2di *in_212 = (__v2di*)&data_in[38 * count_in/64]; __v2di *in_213 = (__v2di*)&data_in[39 * count_in/64]; __v2di *in_220 = (__v2di*)&data_in[40 * count_in/64]; __v2di *in_221 = (__v2di*)&data_in[41 * count_in/64]; __v2di *in_222 = (__v2di*)&data_in[42 * count_in/64]; __v2di *in_223 = (__v2di*)&data_in[43 * count_in/64]; __v2di *in_230 = (__v2di*)&data_in[44 * count_in/64]; __v2di *in_231 = (__v2di*)&data_in[45 * count_in/64]; __v2di *in_232 = (__v2di*)&data_in[46 * count_in/64]; __v2di *in_233 = (__v2di*)&data_in[47 * count_in/64]; __v2di *in_300 = (__v2di*)&data_in[48 * count_in/64]; __v2di *in_301 = (__v2di*)&data_in[49 * count_in/64]; __v2di *in_302 = (__v2di*)&data_in[50 * count_in/64]; __v2di *in_303 = (__v2di*)&data_in[51 * count_in/64]; __v2di *in_310 = (__v2di*)&data_in[52 * count_in/64]; __v2di *in_311 = (__v2di*)&data_in[53 * count_in/64]; __v2di *in_312 = (__v2di*)&data_in[54 * count_in/64]; __v2di *in_313 = (__v2di*)&data_in[55 * count_in/64]; __v2di *in_320 = (__v2di*)&data_in[56 * count_in/64]; __v2di *in_321 = (__v2di*)&data_in[57 * count_in/64]; __v2di *in_322 = (__v2di*)&data_in[58 * count_in/64]; __v2di *in_323 = (__v2di*)&data_in[59 * count_in/64]; __v2di *in_330 = (__v2di*)&data_in[60 * count_in/64]; __v2di *in_331 = (__v2di*)&data_in[61 * count_in/64]; __v2di *in_332 = (__v2di*)&data_in[62 * count_in/64]; __v2di *in_333 = (__v2di*)&data_in[63 * count_in/64]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/32/2; ++i) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 16 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_000[i], in_000[i], mask); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_100[i], in_100[i], mask); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_200[i], in_200[i], mask); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_300[i], in_300[i], mask); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_010[i], in_010[i], mask); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_110[i], in_110[i], mask); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_210[i], in_210[i], mask); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_310[i], in_310[i], mask); *(__v2du*)((void*)data_out + offset + 8*16) = __builtin_e2k_qppermb(in_020[i], in_020[i], mask); *(__v2du*)((void*)data_out + offset + 9*16) = __builtin_e2k_qppermb(in_120[i], in_120[i], mask); *(__v2du*)((void*)data_out + offset + 10*16) = __builtin_e2k_qppermb(in_220[i], in_220[i], mask); *(__v2du*)((void*)data_out + offset + 11*16) = __builtin_e2k_qppermb(in_320[i], in_320[i], mask); *(__v2du*)((void*)data_out + offset + 12*16) = __builtin_e2k_qppermb(in_030[i], in_030[i], mask); *(__v2du*)((void*)data_out + offset + 13*16) = __builtin_e2k_qppermb(in_130[i], in_130[i], mask); *(__v2du*)((void*)data_out + offset + 14*16) = __builtin_e2k_qppermb(in_230[i], in_230[i], mask); *(__v2du*)((void*)data_out + offset + 15*16) = __builtin_e2k_qppermb(in_330[i], in_330[i], mask); *(__v2du*)((void*)data_out + offset + 16*16) = __builtin_e2k_qppermb(in_001[i], in_001[i], mask); *(__v2du*)((void*)data_out + offset + 17*16) = __builtin_e2k_qppermb(in_101[i], in_101[i], mask); *(__v2du*)((void*)data_out + offset + 18*16) = __builtin_e2k_qppermb(in_201[i], in_201[i], mask); *(__v2du*)((void*)data_out + offset + 19*16) = __builtin_e2k_qppermb(in_301[i], in_301[i], mask); *(__v2du*)((void*)data_out + offset + 20*16) = __builtin_e2k_qppermb(in_011[i], in_011[i], mask); *(__v2du*)((void*)data_out + offset + 21*16) = __builtin_e2k_qppermb(in_111[i], in_111[i], mask); *(__v2du*)((void*)data_out + offset + 22*16) = __builtin_e2k_qppermb(in_211[i], in_211[i], mask); *(__v2du*)((void*)data_out + offset + 23*16) = __builtin_e2k_qppermb(in_311[i], in_311[i], mask); *(__v2du*)((void*)data_out + offset + 24*16) = __builtin_e2k_qppermb(in_021[i], in_021[i], mask); *(__v2du*)((void*)data_out + offset + 25*16) = __builtin_e2k_qppermb(in_121[i], in_121[i], mask); *(__v2du*)((void*)data_out + offset + 26*16) = __builtin_e2k_qppermb(in_221[i], in_221[i], mask); *(__v2du*)((void*)data_out + offset + 27*16) = __builtin_e2k_qppermb(in_321[i], in_321[i], mask); *(__v2du*)((void*)data_out + offset + 28*16) = __builtin_e2k_qppermb(in_031[i], in_031[i], mask); *(__v2du*)((void*)data_out + offset + 29*16) = __builtin_e2k_qppermb(in_131[i], in_131[i], mask); *(__v2du*)((void*)data_out + offset + 30*16) = __builtin_e2k_qppermb(in_231[i], in_231[i], mask); *(__v2du*)((void*)data_out + offset + 31*16) = __builtin_e2k_qppermb(in_331[i], in_331[i], mask); } #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/32/2; ++i) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 16 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask = {0x0B0A090803020100, 0x0F0E0D0C07060504}; *(__v2du*)((void*)data_out + offset + 32*16) = __builtin_e2k_qppermb(in_002[i], in_002[i], mask); *(__v2du*)((void*)data_out + offset + 33*16) = __builtin_e2k_qppermb(in_102[i], in_102[i], mask); *(__v2du*)((void*)data_out + offset + 34*16) = __builtin_e2k_qppermb(in_202[i], in_202[i], mask); *(__v2du*)((void*)data_out + offset + 35*16) = __builtin_e2k_qppermb(in_302[i], in_302[i], mask); *(__v2du*)((void*)data_out + offset + 36*16) = __builtin_e2k_qppermb(in_012[i], in_012[i], mask); *(__v2du*)((void*)data_out + offset + 37*16) = __builtin_e2k_qppermb(in_112[i], in_112[i], mask); *(__v2du*)((void*)data_out + offset + 38*16) = __builtin_e2k_qppermb(in_212[i], in_212[i], mask); *(__v2du*)((void*)data_out + offset + 39*16) = __builtin_e2k_qppermb(in_312[i], in_312[i], mask); *(__v2du*)((void*)data_out + offset + 40*16) = __builtin_e2k_qppermb(in_022[i], in_022[i], mask); *(__v2du*)((void*)data_out + offset + 41*16) = __builtin_e2k_qppermb(in_122[i], in_122[i], mask); *(__v2du*)((void*)data_out + offset + 42*16) = __builtin_e2k_qppermb(in_222[i], in_222[i], mask); *(__v2du*)((void*)data_out + offset + 43*16) = __builtin_e2k_qppermb(in_322[i], in_322[i], mask); *(__v2du*)((void*)data_out + offset + 44*16) = __builtin_e2k_qppermb(in_032[i], in_032[i], mask); *(__v2du*)((void*)data_out + offset + 45*16) = __builtin_e2k_qppermb(in_132[i], in_132[i], mask); *(__v2du*)((void*)data_out + offset + 46*16) = __builtin_e2k_qppermb(in_232[i], in_232[i], mask); *(__v2du*)((void*)data_out + offset + 47*16) = __builtin_e2k_qppermb(in_332[i], in_332[i], mask); *(__v2du*)((void*)data_out + offset + 48*16) = __builtin_e2k_qppermb(in_003[i], in_003[i], mask); *(__v2du*)((void*)data_out + offset + 49*16) = __builtin_e2k_qppermb(in_103[i], in_103[i], mask); *(__v2du*)((void*)data_out + offset + 50*16) = __builtin_e2k_qppermb(in_203[i], in_203[i], mask); *(__v2du*)((void*)data_out + offset + 51*16) = __builtin_e2k_qppermb(in_303[i], in_303[i], mask); *(__v2du*)((void*)data_out + offset + 52*16) = __builtin_e2k_qppermb(in_013[i], in_013[i], mask); *(__v2du*)((void*)data_out + offset + 53*16) = __builtin_e2k_qppermb(in_113[i], in_113[i], mask); *(__v2du*)((void*)data_out + offset + 54*16) = __builtin_e2k_qppermb(in_213[i], in_213[i], mask); *(__v2du*)((void*)data_out + offset + 55*16) = __builtin_e2k_qppermb(in_313[i], in_313[i], mask); *(__v2du*)((void*)data_out + offset + 56*16) = __builtin_e2k_qppermb(in_023[i], in_023[i], mask); *(__v2du*)((void*)data_out + offset + 57*16) = __builtin_e2k_qppermb(in_123[i], in_123[i], mask); *(__v2du*)((void*)data_out + offset + 58*16) = __builtin_e2k_qppermb(in_223[i], in_223[i], mask); *(__v2du*)((void*)data_out + offset + 59*16) = __builtin_e2k_qppermb(in_323[i], in_323[i], mask); *(__v2du*)((void*)data_out + offset + 60*16) = __builtin_e2k_qppermb(in_033[i], in_033[i], mask); *(__v2du*)((void*)data_out + offset + 61*16) = __builtin_e2k_qppermb(in_133[i], in_133[i], mask); *(__v2du*)((void*)data_out + offset + 62*16) = __builtin_e2k_qppermb(in_233[i], in_233[i], mask); *(__v2du*)((void*)data_out + offset + 63*16) = __builtin_e2k_qppermb(in_333[i], in_333[i], mask); } }
Основной цикл на ассемблере
.L9991: { fapb ct=0, dcd=0, fmt=5, mrng=16, d=17, incr=0, ind=0, asz=1, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=16, incr=0, ind=0, asz=1, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=15, incr=0, ind=0, asz=1, abs=2, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=14, incr=0, ind=0, asz=1, abs=2, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=13, incr=0, ind=0, asz=1, abs=4, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=12, incr=0, ind=0, asz=1, abs=4, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=11, incr=0, ind=0, asz=1, abs=6, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=10, incr=0, ind=0, asz=1, abs=6, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=9, incr=0, ind=0, asz=1, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=8, incr=0, ind=0, asz=1, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=7, incr=0, ind=0, asz=1, abs=10, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=6, incr=0, ind=0, asz=1, abs=10, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=5, incr=0, ind=0, asz=1, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=4, incr=0, ind=0, asz=1, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=3, incr=0, ind=0, asz=1, abs=14, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=2, incr=0, ind=0, asz=1, abs=14, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=1, incr=0, ind=0, asz=1, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=15, asz=1, abs=16, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=14, asz=1, abs=18, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=13, asz=1, abs=18, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=12, asz=1, abs=20, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=11, asz=1, abs=20, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=10, asz=1, abs=22, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=9, asz=1, abs=22, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=8, asz=1, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=7, asz=1, abs=24, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=6, asz=1, abs=26, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=5, asz=1, abs=26, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=4, asz=1, abs=28, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=3, asz=1, abs=28, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=2, asz=1, abs=30, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=1, asz=1, abs=30, disp=0 } .L6665: { loop_mode qppermb,0,sm %b[41], %b[41], %r0, %b[58] shr_andd,1,sm %b[56], %r1, %r38, %b[54] stqp,2 %r5, %b[34], %b[54] qppermb,3,sm %b[46], %b[46], %r0, %b[60] shr_andd,4,sm %b[56], %r4, %r37, %b[56] stqp,5 %r2, %b[34], %b[58] } { loop_mode qppermb,1,sm %b[76], %b[76], %r0, %b[1] stqp,2 %r7, %b[34], %b[58] qppermb,4,sm %b[77], %b[77], %r0, %b[8] stqp,5 %r6, %b[34], %b[60] } { loop_mode qppermb,1,sm %b[65], %b[65], %r0, %b[10] stqp,2 %r11, %b[34], %b[3] qppermb,4,sm %b[63], %b[63], %r0, %b[3] stqp,5 %r9, %b[34], %b[10] } { loop_mode qppermb,1,sm %b[75], %b[75], %r0, %b[12] stqp,2 %r10, %b[34], %b[12] qppermb,4,sm %b[74], %b[74], %r0, %b[5] stqp,5 %r12, %b[34], %b[5] } { loop_mode qppermb,1,sm %b[73], %b[73], %r0, %b[14] stqp,2 %r13, %b[34], %b[14] qppermb,4,sm %b[72], %b[72], %r0, %b[7] stqp,5 %r14, %b[34], %b[7] } { loop_mode qppermb,1,sm %b[71], %b[71], %r0, %b[16] stqp,2 %r15, %b[34], %b[16] qppermb,4,sm %b[70], %b[70], %r0, %b[9] stqp,5 %r16, %b[34], %b[9] } { loop_mode qppermb,1,sm %b[69], %b[69], %r0, %b[18] stqp,2 %r17, %b[34], %b[18] qppermb,4,sm %b[67], %b[67], %r0, %b[11] stqp,5 %r18, %b[34], %b[11] movaqp,0 area=15, ind=0, am=1, be=0, %b[60] movaqp,1 area=14, ind=0, am=1, be=0, %b[64] movaqp,2 area=15, ind=0, am=1, be=0, %b[58] movaqp,3 area=14, ind=0, am=1, be=0, %b[62] } { loop_mode qppermb,1,sm %b[68], %b[68], %r0, %b[36] stqp,2 %r19, %b[34], %b[20] qppermb,4,sm %b[61], %b[61], %r0, %b[31] stqp,5 %r20, %b[34], %b[13] movaqp,0 area=13, ind=0, am=1, be=0, %b[63] movaqp,1 area=12, ind=0, am=1, be=0, %b[20] movaqp,2 area=13, ind=0, am=1, be=0, %b[61] movaqp,3 area=12, ind=0, am=1, be=0, %b[13] } { loop_mode qppermb,1,sm %b[59], %b[59], %r0, %b[53] stqp,2 %r21, %b[34], %b[38] qppermb,4,sm %b[57], %b[57], %r0, %b[49] stqp,5 %r22, %b[34], %b[33] movaqp,0 area=11, ind=0, am=1, be=0, %b[38] movaqp,1 area=10, ind=0, am=1, be=0, %b[46] movaqp,2 area=11, ind=0, am=1, be=0, %b[33] movaqp,3 area=10, ind=0, am=1, be=0, %b[41] } { loop_mode qppermb,1,sm %b[48], %b[48], %r0, %b[48] stqp,2 %r23, %b[34], %b[55] qppermb,4,sm %b[43], %b[43], %r0, %b[43] stqp,5 %r24, %b[34], %b[51] movaqp,0 area=9, ind=0, am=1, be=0, %b[57] movaqp,1 area=8, ind=0, am=1, be=0, %b[66] movaqp,2 area=9, ind=0, am=1, be=0, %b[55] movaqp,3 area=8, ind=0, am=1, be=0, %b[59] } { loop_mode qppermb,1,sm %b[40], %b[40], %r0, %b[40] stqp,2 %r25, %b[34], %b[50] qppermb,4,sm %b[35], %b[35], %r0, %b[35] stqp,5 %r26, %b[34], %b[45] movaqp,0 area=7, ind=0, am=1, be=0, %b[67] movaqp,1 area=6, ind=0, am=1, be=0, %b[69] movaqp,2 area=7, ind=0, am=1, be=0, %b[65] movaqp,3 area=6, ind=0, am=1, be=0, %b[68] } { loop_mode qppermb,1,sm %b[22], %b[22], %r0, %b[22] stqp,2 %r27, %b[34], %b[42] qppermb,4,sm %b[15], %b[15], %r0, %b[15] stqp,5 %r28, %b[34], %b[37] movaqp,0 area=5, ind=0, am=1, be=0, %b[71] movaqp,1 area=4, ind=0, am=1, be=0, %b[73] movaqp,2 area=5, ind=0, am=1, be=0, %b[70] movaqp,3 area=4, ind=0, am=1, be=0, %b[72] } { loop_mode qppermb,1,sm %b[63], %b[63], %r0, %b[24] stqp,2 %r29, %b[34], %b[24] qppermb,4,sm %b[61], %b[61], %r0, %b[17] stqp,5 %r30, %b[34], %b[17] movaqp,0 area=3, ind=0, am=1, be=0, %b[74] movaqp,1 area=2, ind=0, am=1, be=0, %b[75] movaqp,2 area=3, ind=0, am=1, be=0, %b[61] movaqp,3 area=2, ind=0, am=1, be=0, %b[63] } { loop_mode addd,0,sm %b[2], 0x1, %b[0] qppermb,1,sm %b[64], %b[64], %r0, %b[28] stqp,2 %r31, %b[34], %b[28] ord,3,sm %b[56], %b[54], %b[54] qppermb,4,sm %b[62], %b[62], %r0, %b[21] stqp,5 %r32, %b[34], %b[21] movaqp,0 area=1, ind=0, am=1, be=0, %b[42] movaqp,1 area=0, ind=0, am=1, be=0, %b[50] movaqp,2 area=1, ind=0, am=1, be=0, %b[37] movaqp,3 area=0, ind=0, am=1, be=0, %b[45] } { loop_mode bitrevd,0,sm %b[2], %b[54] qppermb,1,sm %b[60], %b[60], %r0, %b[25] stqp,2 %r33, %b[34], %b[32] shld,3,sm %b[54], 0x4, %b[32] qppermb,4,sm %b[58], %b[58], %r0, %b[2] stqp,5 %r34, %b[34], %b[25] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[47], %b[47], %r0, %b[52] stqp,2 %r35, %b[34], %b[29] qppermb,3,sm %b[52], %b[52], %r0, %b[56] stqp,5 %r36, %b[34], %b[6] } ... .L9337: { fapb ct=0, dcd=0, fmt=5, mrng=16, d=17, incr=0, ind=0, asz=1, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=16, incr=0, ind=0, asz=1, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=15, incr=0, ind=0, asz=1, abs=2, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=14, incr=0, ind=0, asz=1, abs=2, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=13, incr=0, ind=0, asz=1, abs=4, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=12, incr=0, ind=0, asz=1, abs=4, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=11, incr=0, ind=0, asz=1, abs=6, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=10, incr=0, ind=0, asz=1, abs=6, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=9, incr=0, ind=0, asz=1, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=8, incr=0, ind=0, asz=1, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=7, incr=0, ind=0, asz=1, abs=10, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=6, incr=0, ind=0, asz=1, abs=10, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=5, incr=0, ind=0, asz=1, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=4, incr=0, ind=0, asz=1, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=3, incr=0, ind=0, asz=1, abs=14, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=2, incr=0, ind=0, asz=1, abs=14, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=1, incr=0, ind=0, asz=1, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=15, asz=1, abs=16, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=14, asz=1, abs=18, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=13, asz=1, abs=18, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=12, asz=1, abs=20, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=11, asz=1, abs=20, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=10, asz=1, abs=22, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=9, asz=1, abs=22, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=8, asz=1, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=7, asz=1, abs=24, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=6, asz=1, abs=26, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=5, asz=1, abs=26, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=4, asz=1, abs=28, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=3, asz=1, abs=28, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=2, asz=1, abs=30, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=1, asz=1, abs=30, disp=0 } .L7373: { loop_mode qppermb,0,sm %b[41], %b[41], %r0, %b[58] shr_andd,1,sm %b[56], %r1, %r38, %b[54] stqp,2 %r5, %b[34], %b[54] qppermb,3,sm %b[46], %b[46], %r0, %b[60] shr_andd,4,sm %b[56], %r4, %r37, %b[56] stqp,5 %r2, %b[34], %b[58] } { loop_mode qppermb,1,sm %b[76], %b[76], %r0, %b[1] stqp,2 %r7, %b[34], %b[58] qppermb,4,sm %b[77], %b[77], %r0, %b[8] stqp,5 %r6, %b[34], %b[60] } { loop_mode qppermb,1,sm %b[65], %b[65], %r0, %b[10] stqp,2 %r11, %b[34], %b[3] qppermb,4,sm %b[63], %b[63], %r0, %b[3] stqp,5 %r9, %b[34], %b[10] } { loop_mode qppermb,1,sm %b[75], %b[75], %r0, %b[12] stqp,2 %r10, %b[34], %b[12] qppermb,4,sm %b[74], %b[74], %r0, %b[5] stqp,5 %r12, %b[34], %b[5] } { loop_mode qppermb,1,sm %b[73], %b[73], %r0, %b[14] stqp,2 %r13, %b[34], %b[14] qppermb,4,sm %b[72], %b[72], %r0, %b[7] stqp,5 %r14, %b[34], %b[7] } { loop_mode qppermb,1,sm %b[71], %b[71], %r0, %b[16] stqp,2 %r15, %b[34], %b[16] qppermb,4,sm %b[70], %b[70], %r0, %b[9] stqp,5 %r16, %b[34], %b[9] } { loop_mode qppermb,1,sm %b[69], %b[69], %r0, %b[18] stqp,2 %r17, %b[34], %b[18] qppermb,4,sm %b[67], %b[67], %r0, %b[11] stqp,5 %r18, %b[34], %b[11] movaqp,0 area=15, ind=0, am=1, be=0, %b[60] movaqp,1 area=14, ind=0, am=1, be=0, %b[64] movaqp,2 area=15, ind=0, am=1, be=0, %b[58] movaqp,3 area=14, ind=0, am=1, be=0, %b[62] } { loop_mode qppermb,1,sm %b[68], %b[68], %r0, %b[36] stqp,2 %r19, %b[34], %b[20] qppermb,4,sm %b[61], %b[61], %r0, %b[31] stqp,5 %r20, %b[34], %b[13] movaqp,0 area=13, ind=0, am=1, be=0, %b[63] movaqp,1 area=12, ind=0, am=1, be=0, %b[20] movaqp,2 area=13, ind=0, am=1, be=0, %b[61] movaqp,3 area=12, ind=0, am=1, be=0, %b[13] } { loop_mode qppermb,1,sm %b[59], %b[59], %r0, %b[53] stqp,2 %r21, %b[34], %b[38] qppermb,4,sm %b[57], %b[57], %r0, %b[49] stqp,5 %r22, %b[34], %b[33] movaqp,0 area=11, ind=0, am=1, be=0, %b[38] movaqp,1 area=10, ind=0, am=1, be=0, %b[46] movaqp,2 area=11, ind=0, am=1, be=0, %b[33] movaqp,3 area=10, ind=0, am=1, be=0, %b[41] } { loop_mode qppermb,1,sm %b[48], %b[48], %r0, %b[48] stqp,2 %r23, %b[34], %b[55] qppermb,4,sm %b[43], %b[43], %r0, %b[43] stqp,5 %r24, %b[34], %b[51] movaqp,0 area=9, ind=0, am=1, be=0, %b[57] movaqp,1 area=8, ind=0, am=1, be=0, %b[66] movaqp,2 area=9, ind=0, am=1, be=0, %b[55] movaqp,3 area=8, ind=0, am=1, be=0, %b[59] } { loop_mode qppermb,1,sm %b[40], %b[40], %r0, %b[40] stqp,2 %r25, %b[34], %b[50] qppermb,4,sm %b[35], %b[35], %r0, %b[35] stqp,5 %r26, %b[34], %b[45] movaqp,0 area=7, ind=0, am=1, be=0, %b[67] movaqp,1 area=6, ind=0, am=1, be=0, %b[69] movaqp,2 area=7, ind=0, am=1, be=0, %b[65] movaqp,3 area=6, ind=0, am=1, be=0, %b[68] } { loop_mode qppermb,1,sm %b[22], %b[22], %r0, %b[22] stqp,2 %r27, %b[34], %b[42] qppermb,4,sm %b[15], %b[15], %r0, %b[15] stqp,5 %r28, %b[34], %b[37] movaqp,0 area=5, ind=0, am=1, be=0, %b[71] movaqp,1 area=4, ind=0, am=1, be=0, %b[73] movaqp,2 area=5, ind=0, am=1, be=0, %b[70] movaqp,3 area=4, ind=0, am=1, be=0, %b[72] } { loop_mode qppermb,1,sm %b[63], %b[63], %r0, %b[24] stqp,2 %r29, %b[34], %b[24] qppermb,4,sm %b[61], %b[61], %r0, %b[17] stqp,5 %r30, %b[34], %b[17] movaqp,0 area=3, ind=0, am=1, be=0, %b[74] movaqp,1 area=2, ind=0, am=1, be=0, %b[75] movaqp,2 area=3, ind=0, am=1, be=0, %b[61] movaqp,3 area=2, ind=0, am=1, be=0, %b[63] } { loop_mode addd,0,sm %b[2], 0x1, %b[0] qppermb,1,sm %b[64], %b[64], %r0, %b[28] stqp,2 %r31, %b[34], %b[28] ord,3,sm %b[56], %b[54], %b[54] qppermb,4,sm %b[62], %b[62], %r0, %b[21] stqp,5 %r32, %b[34], %b[21] movaqp,0 area=1, ind=0, am=1, be=0, %b[42] movaqp,1 area=0, ind=0, am=1, be=0, %b[50] movaqp,2 area=1, ind=0, am=1, be=0, %b[37] movaqp,3 area=0, ind=0, am=1, be=0, %b[45] } { loop_mode bitrevd,0,sm %b[2], %b[54] qppermb,1,sm %b[60], %b[60], %r0, %b[25] stqp,2 %r33, %b[34], %b[32] shld,3,sm %b[54], 0x4, %b[32] qppermb,4,sm %b[58], %b[58], %r0, %b[2] stqp,5 %r34, %b[34], %b[25] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[47], %b[47], %r0, %b[52] stqp,2 %r35, %b[34], %b[29] qppermb,3,sm %b[52], %b[52], %r0, %b[56] stqp,5 %r36, %b[34], %b[6] }
Теоретическая скорость: 64 комплексных числа за 16 тактов (64/16) = 32 Байт/такт
Замеры скорости

Видим замедление по всей длине графика.
Итоги по reverse_radix4_vector2


Победителем можно считать reverse_radix4_vector2_stream16.
При реализации Vector-2 Radix-4 FFT будем использовать его.
reverse_radix4_vector4
1. reverse_radix4_vector4_etalon
Эталонный вариант для сравнения на корректность.
Здесь reverseNumber_radix4 вычисляется с помощью цикла.
int reverseNumber_radix4(int number, int bit_count) { int answer = 0; for(int i = 0; i < bit_count/2; ++i) { answer <<= 2; answer |= number & 3; number >>= 2; } return answer; } void reverse_radix4_vector4_etalon(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; for(int64_t i = 0; i < count_out; ++i) { int index = reverseNumber_radix4(i, bit_count_out); myComplex a = data_in[4*i + 0]; myComplex b = data_in[4*i + 1]; myComplex c = data_in[4*i + 2]; myComplex d = data_in[4*i + 3]; data_out[index] = (myComplex4){.real = {a.real, b.real, c.real, d.real}, .imag = {a.imag, b.imag, c.imag, d.imag}}; } }
2. reverse_radix4_vector4
В процессоре нет готовых инструкций, производящих операцию reverseNumber_radix4.
Поэтому выполним инструкцию bitrevd и переставим соседние биты местами.
Код на Си
void reverse_radix4_vector4(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in = (__v2di*)data_in; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out; ++i) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 32 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in[2*i+1], in[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in[2*i+1], in[2*i+0], mask_imag); } }
Основной цикл на ассемблере
.L6561: { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 } .L6351: { loop_mode qppermb,0,sm %b[15], %b[12], %r4, %b[19] shr_andd,1,sm %b[14], %r6, %r10, %b[0] qppermb,3,sm %b[15], %b[12], %r5, %b[20] shr_andd,4,sm %b[16], %r7, %r9, %b[5] ord,5,sm %b[7], %b[4], %b[18] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END bitrevd,0,sm %b[3], %b[12] stqp,2 %r0, %b[17], %b[19] addd,3,sm %b[3], 0x1, %b[1] shld,4,sm %b[18], 0x5, %b[15] stqp,5 %r2, %b[17], %b[20] movaqp,0 area=0, ind=0, am=1, be=0, %b[4] movaqp,1 area=0, ind=16, am=0, be=0, %b[7] }
Теоретическая скорость: 4 комплексных числа за 2 такта (4/2) = 16 Байт/такт
Замеры скорости

3. reverse_radix4_vector4_x4
Сделаем ручную раскрутку цикла в 4 раза.
Код на Си
void reverse_radix4_vector4_x4(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; // int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in = (__v2di*) data_in; __v2di *out_0 = (__v2di*)&data_out[0 * count_out/4]; __v2di *out_1 = (__v2di*)&data_out[1 * count_out/4]; __v2di *out_2 = (__v2di*)&data_out[2 * count_out/4]; __v2di *out_3 = (__v2di*)&data_out[3 * count_out/4]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out; i += 4) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 32 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)out_0 + offset + 0*16) = __builtin_e2k_qppermb(in[2*(i+0)+1], in[2*(i+0)+0], mask_real); *(__v2du*)((void*)out_0 + offset + 1*16) = __builtin_e2k_qppermb(in[2*(i+0)+1], in[2*(i+0)+0], mask_imag); *(__v2du*)((void*)out_1 + offset + 0*16) = __builtin_e2k_qppermb(in[2*(i+1)+1], in[2*(i+1)+0], mask_real); *(__v2du*)((void*)out_1 + offset + 1*16) = __builtin_e2k_qppermb(in[2*(i+1)+1], in[2*(i+1)+0], mask_imag); *(__v2du*)((void*)out_2 + offset + 0*16) = __builtin_e2k_qppermb(in[2*(i+2)+1], in[2*(i+2)+0], mask_real); *(__v2du*)((void*)out_2 + offset + 1*16) = __builtin_e2k_qppermb(in[2*(i+2)+1], in[2*(i+2)+0], mask_imag); *(__v2du*)((void*)out_3 + offset + 0*16) = __builtin_e2k_qppermb(in[2*(i+3)+1], in[2*(i+3)+0], mask_real); *(__v2du*)((void*)out_3 + offset + 1*16) = __builtin_e2k_qppermb(in[2*(i+3)+1], in[2*(i+3)+0], mask_imag); } }
Основной цикл на ассемблере
.L7192: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=4, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=4, abs=0, disp=32 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=4, abs=16, disp=64 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=4, abs=16, disp=96 } .L6608: { loop_mode qppermb,0,sm %b[23], %b[9], %r11, %b[35] stqp,2 %r0, %b[33], %b[22] qppermb,3,sm %b[23], %b[9], %r13, %b[36] shr_andd,4,sm %b[12], %r14, %r16, %b[1] stqp,5 %r2, %b[33], %b[25] } { loop_mode ord,0,sm %b[17], %b[3], %b[23] qppermb,1,sm %b[15], %b[10], %r11, %b[37] stqp,2 %r4, %b[33], %b[35] qppermb,4,sm %b[28], %b[20], %r13, %b[38] stqp,5 %r10, %b[33], %b[36] movaqp,0 area=1, ind=0, am=1, be=0, %b[14] movaqp,1 area=1, ind=16, am=0, be=0, %b[22] movaqp,2 area=1, ind=0, am=1, be=0, %b[4] movaqp,3 area=1, ind=16, am=0, be=0, %b[9] } { loop_mode shld,0,sm %b[23], 0x5, %b[31] qppermb,1,sm %b[28], %b[20], %r11, %b[36] stqp,2 %r6, %b[33], %b[37] addd,3,sm %b[2], 0x4, %b[0] qppermb,4,sm %b[15], %b[10], %r13, %b[35] stqp,5 %r9, %b[33], %b[38] movaqp,0 area=0, ind=0, am=1, be=0, %b[25] movaqp,1 area=0, ind=16, am=0, be=0, %b[30] movaqp,2 area=0, ind=0, am=1, be=0, %b[3] movaqp,3 area=0, ind=16, am=0, be=0, %b[17] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[34], %b[29], %r11, %b[20] shr_andd,1,sm %b[12], %r12, %r15, %b[15] stqp,2 %r5, %b[33], %b[36] qppermb,3,sm %b[34], %b[29], %r13, %b[23] bitrevd,4,sm %b[2], %b[10] stqp,5 %r7, %b[33], %b[35] }
Теоретическая скорость: 16 комплексных чисел за 4 такта (16/4) = 32 Байт/такт
Замеры скорости

Видим ускорение в начале и замедление в конце графика.
Здесь происходит четыре чтения из одного места памяти и запись в четыре разных места памяти.
4. reverse_radix4_vector4_stream4
Теперь сделаем наоборот: будем читать из четырёх разных мест, а писать рядом.
Чтение в 4 потока из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix4_vector4_stream4(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_0 = (__v2di*)&data_in[0 * count_in/4]; __v2di *in_1 = (__v2di*)&data_in[1 * count_in/4]; __v2di *in_2 = (__v2di*)&data_in[2 * count_in/4]; __v2di *in_3 = (__v2di*)&data_in[3 * count_in/4]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/4; ++i) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 32 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_0[2*i+1], in_0[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_0[2*i+1], in_0[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_1[2*i+1], in_1[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_1[2*i+1], in_1[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_2[2*i+1], in_2[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_2[2*i+1], in_2[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_3[2*i+1], in_3[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_3[2*i+1], in_3[2*i+0], mask_imag); } }
Основной цикл на ассемблере
.L7740: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=4, asz=4, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=3, asz=4, abs=0, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=2, asz=4, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=4, abs=16, disp=0 } .L7235: { loop_mode qppermb,0,sm %b[23], %b[9], %r11, %b[35] stqp,2 %r0, %b[33], %b[22] qppermb,3,sm %b[23], %b[9], %r13, %b[36] shr_andd,4,sm %b[12], %r14, %r16, %b[1] stqp,5 %r2, %b[33], %b[25] } { loop_mode ord,0,sm %b[17], %b[3], %b[23] qppermb,1,sm %b[15], %b[10], %r11, %b[37] stqp,2 %r5, %b[33], %b[35] qppermb,4,sm %b[28], %b[20], %r13, %b[38] stqp,5 %r4, %b[33], %b[36] movaqp,0 area=1, ind=0, am=1, be=0, %b[14] movaqp,1 area=1, ind=16, am=0, be=0, %b[22] movaqp,2 area=1, ind=0, am=1, be=0, %b[4] movaqp,3 area=1, ind=16, am=0, be=0, %b[9] } { loop_mode shld,0,sm %b[23], 0x5, %b[31] qppermb,1,sm %b[28], %b[20], %r11, %b[36] stqp,2 %r10, %b[33], %b[37] addd,3,sm %b[2], 0x1, %b[0] qppermb,4,sm %b[15], %b[10], %r13, %b[35] stqp,5 %r6, %b[33], %b[38] movaqp,0 area=0, ind=0, am=1, be=0, %b[25] movaqp,1 area=0, ind=16, am=0, be=0, %b[30] movaqp,2 area=0, ind=0, am=1, be=0, %b[3] movaqp,3 area=0, ind=16, am=0, be=0, %b[17] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[34], %b[29], %r11, %b[20] shr_andd,1,sm %b[12], %r12, %r15, %b[15] stqp,2 %r7, %b[33], %b[36] qppermb,3,sm %b[34], %b[29], %r13, %b[23] bitrevd,4,sm %b[2], %b[10] stqp,5 %r9, %b[33], %b[35] }
Теоретическая скорость: 16 комплексных чисел за 4 такта (16/4) = 32 Байт/такт
Замеры скорости

Видим желаемое ускорение по всей длине графика.
5. reverse_radix4_vector4_stream16
Чтение в 16 потоков из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix4_vector4_stream16(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_00 = (__v2di*)&data_in[ 0 * count_in/16]; __v2di *in_01 = (__v2di*)&data_in[ 1 * count_in/16]; __v2di *in_02 = (__v2di*)&data_in[ 2 * count_in/16]; __v2di *in_03 = (__v2di*)&data_in[ 3 * count_in/16]; __v2di *in_10 = (__v2di*)&data_in[ 4 * count_in/16]; __v2di *in_11 = (__v2di*)&data_in[ 5 * count_in/16]; __v2di *in_12 = (__v2di*)&data_in[ 6 * count_in/16]; __v2di *in_13 = (__v2di*)&data_in[ 7 * count_in/16]; __v2di *in_20 = (__v2di*)&data_in[ 8 * count_in/16]; __v2di *in_21 = (__v2di*)&data_in[ 9 * count_in/16]; __v2di *in_22 = (__v2di*)&data_in[10 * count_in/16]; __v2di *in_23 = (__v2di*)&data_in[11 * count_in/16]; __v2di *in_30 = (__v2di*)&data_in[12 * count_in/16]; __v2di *in_31 = (__v2di*)&data_in[13 * count_in/16]; __v2di *in_32 = (__v2di*)&data_in[14 * count_in/16]; __v2di *in_33 = (__v2di*)&data_in[15 * count_in/16]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/16; ++i) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 32 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_00[2*i+1], in_00[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_00[2*i+1], in_00[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_10[2*i+1], in_10[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_10[2*i+1], in_10[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_20[2*i+1], in_20[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_20[2*i+1], in_20[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_30[2*i+1], in_30[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_30[2*i+1], in_30[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 8*16) = __builtin_e2k_qppermb(in_01[2*i+1], in_01[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 9*16) = __builtin_e2k_qppermb(in_01[2*i+1], in_01[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 10*16) = __builtin_e2k_qppermb(in_11[2*i+1], in_11[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 11*16) = __builtin_e2k_qppermb(in_11[2*i+1], in_11[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 12*16) = __builtin_e2k_qppermb(in_21[2*i+1], in_21[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 13*16) = __builtin_e2k_qppermb(in_21[2*i+1], in_21[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 14*16) = __builtin_e2k_qppermb(in_31[2*i+1], in_31[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 15*16) = __builtin_e2k_qppermb(in_31[2*i+1], in_31[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 16*16) = __builtin_e2k_qppermb(in_02[2*i+1], in_02[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 17*16) = __builtin_e2k_qppermb(in_02[2*i+1], in_02[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 18*16) = __builtin_e2k_qppermb(in_12[2*i+1], in_12[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 19*16) = __builtin_e2k_qppermb(in_12[2*i+1], in_12[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 20*16) = __builtin_e2k_qppermb(in_22[2*i+1], in_22[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 21*16) = __builtin_e2k_qppermb(in_22[2*i+1], in_22[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 22*16) = __builtin_e2k_qppermb(in_32[2*i+1], in_32[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 23*16) = __builtin_e2k_qppermb(in_32[2*i+1], in_32[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 24*16) = __builtin_e2k_qppermb(in_03[2*i+1], in_03[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 25*16) = __builtin_e2k_qppermb(in_03[2*i+1], in_03[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 26*16) = __builtin_e2k_qppermb(in_13[2*i+1], in_13[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 27*16) = __builtin_e2k_qppermb(in_13[2*i+1], in_13[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 28*16) = __builtin_e2k_qppermb(in_23[2*i+1], in_23[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 29*16) = __builtin_e2k_qppermb(in_23[2*i+1], in_23[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 30*16) = __builtin_e2k_qppermb(in_33[2*i+1], in_33[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 31*16) = __builtin_e2k_qppermb(in_33[2*i+1], in_33[2*i+0], mask_imag); } }
Основной цикл на ассемблере
.L9557: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=1, incr=1, ind=0, asz=2, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=15, asz=2, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=14, asz=2, abs=4, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=13, asz=2, abs=4, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=12, asz=2, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=11, asz=2, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=10, asz=2, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=9, asz=2, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=8, asz=2, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=7, asz=2, abs=16, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=6, asz=2, abs=20, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=5, asz=2, abs=20, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=4, asz=2, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=3, asz=2, abs=24, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=2, asz=2, abs=28, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=28, disp=0 } .L7879: { loop_mode qppermb,0,sm %b[20], %b[15], %r36, %b[73] shr_andd,1,sm %b[69], %r37, %r40, %b[69] stqp,2 %r0, %b[4], %b[71] qppermb,3,sm %b[20], %b[15], %r35, %b[72] shr_andd,4,sm %b[69], %r38, %r39, %b[71] stqp,5 %r2, %b[4], %b[72] } { loop_mode qppermb,1,sm %b[28], %b[23], %r36, %b[72] stqp,2 %r5, %b[4], %b[73] qppermb,4,sm %b[67], %b[64], %r35, %b[74] stqp,5 %r1, %b[4], %b[72] } { loop_mode qppermb,1,sm %b[67], %b[64], %r36, %b[72] stqp,2 %r10, %b[4], %b[72] qppermb,4,sm %b[28], %b[23], %r35, %b[67] stqp,5 %r6, %b[4], %b[74] } { loop_mode qppermb,1,sm %b[63], %b[60], %r36, %b[67] stqp,2 %r7, %b[4], %b[72] qppermb,4,sm %b[63], %b[60], %r35, %b[72] stqp,5 %r9, %b[4], %b[67] } { loop_mode qppermb,1,sm %b[36], %b[31], %r36, %b[67] stqp,2 %r12, %b[4], %b[67] qppermb,4,sm %b[36], %b[31], %r35, %b[72] stqp,5 %r11, %b[4], %b[72] } { loop_mode qppermb,1,sm %b[59], %b[56], %r36, %b[67] stqp,2 %r14, %b[4], %b[67] qppermb,4,sm %b[59], %b[56], %r35, %b[72] stqp,5 %r13, %b[4], %b[72] } { loop_mode qppermb,1,sm %b[44], %b[39], %r36, %b[67] stqp,2 %r16, %b[4], %b[67] qppermb,4,sm %b[44], %b[39], %r35, %b[72] stqp,5 %r15, %b[4], %b[72] movaqp,0 area=7, ind=0, am=1, be=0, %b[7] movaqp,1 area=7, ind=16, am=0, be=0, %b[12] movaqp,2 area=7, ind=0, am=1, be=0, %b[1] movaqp,3 area=7, ind=16, am=0, be=0, %b[6] } { loop_mode qppermb,1,sm %b[55], %b[52], %r36, %b[67] stqp,2 %r18, %b[4], %b[67] qppermb,4,sm %b[55], %b[52], %r35, %b[72] stqp,5 %r17, %b[4], %b[72] movaqp,0 area=6, ind=0, am=1, be=0, %b[23] movaqp,1 area=6, ind=16, am=0, be=0, %b[28] movaqp,2 area=6, ind=0, am=1, be=0, %b[15] movaqp,3 area=6, ind=16, am=0, be=0, %b[20] } { loop_mode qppermb,1,sm %b[51], %b[47], %r36, %b[67] stqp,2 %r20, %b[4], %b[67] qppermb,4,sm %b[51], %b[47], %r35, %b[72] stqp,5 %r19, %b[4], %b[72] movaqp,0 area=5, ind=0, am=1, be=0, %b[39] movaqp,1 area=5, ind=16, am=0, be=0, %b[44] movaqp,2 area=5, ind=0, am=1, be=0, %b[31] movaqp,3 area=5, ind=16, am=0, be=0, %b[36] } { loop_mode qppermb,1,sm %b[48], %b[43], %r36, %b[67] stqp,2 %r22, %b[4], %b[67] qppermb,4,sm %b[48], %b[43], %r35, %b[72] stqp,5 %r21, %b[4], %b[72] movaqp,0 area=4, ind=0, am=1, be=0, %b[48] movaqp,1 area=4, ind=16, am=0, be=0, %b[51] movaqp,2 area=4, ind=0, am=1, be=0, %b[43] movaqp,3 area=4, ind=16, am=0, be=0, %b[47] } { loop_mode qppermb,1,sm %b[40], %b[35], %r36, %b[67] stqp,2 %r24, %b[4], %b[67] qppermb,4,sm %b[40], %b[35], %r35, %b[72] stqp,5 %r23, %b[4], %b[72] movaqp,0 area=3, ind=0, am=1, be=0, %b[52] movaqp,1 area=3, ind=16, am=0, be=0, %b[55] movaqp,2 area=3, ind=0, am=1, be=0, %b[35] movaqp,3 area=3, ind=16, am=0, be=0, %b[40] } { loop_mode qppermb,1,sm %b[32], %b[27], %r36, %b[67] stqp,2 %r26, %b[4], %b[67] qppermb,4,sm %b[32], %b[27], %r35, %b[72] stqp,5 %r25, %b[4], %b[72] movaqp,0 area=2, ind=0, am=1, be=0, %b[56] movaqp,1 area=2, ind=16, am=0, be=0, %b[59] movaqp,2 area=2, ind=0, am=1, be=0, %b[27] movaqp,3 area=2, ind=16, am=0, be=0, %b[32] } { loop_mode qppermb,1,sm %b[24], %b[19], %r36, %b[67] stqp,2 %r28, %b[4], %b[67] qppermb,4,sm %b[24], %b[19], %r35, %b[72] stqp,5 %r27, %b[4], %b[72] movaqp,0 area=1, ind=0, am=1, be=0, %b[60] movaqp,1 area=1, ind=16, am=0, be=0, %b[63] movaqp,2 area=1, ind=0, am=1, be=0, %b[19] movaqp,3 area=1, ind=16, am=0, be=0, %b[24] } { loop_mode addd,0,sm %b[2], 0x1, %b[0] qppermb,1,sm %b[16], %b[11], %r36, %b[69] stqp,2 %r30, %b[4], %b[67] ord,3,sm %b[71], %b[69], %b[67] qppermb,4,sm %b[16], %b[11], %r35, %b[71] stqp,5 %r29, %b[4], %b[72] movaqp,0 area=0, ind=0, am=1, be=0, %b[64] movaqp,1 area=0, ind=16, am=0, be=0, %b[68] movaqp,2 area=0, ind=0, am=1, be=0, %b[11] movaqp,3 area=0, ind=16, am=0, be=0, %b[16] } { loop_mode bitrevd,0,sm %b[2], %b[67] qppermb,1,sm %b[10], %b[5], %r36, %b[69] stqp,2 %r32, %b[4], %b[69] shld,3,sm %b[67], 0x5, %b[2] qppermb,4,sm %b[10], %b[5], %r35, %b[71] stqp,5 %r31, %b[4], %b[71] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qppermb,0,sm %b[70], %b[66], %r36, %b[69] stqp,2 %r34, %b[4], %b[69] qppermb,3,sm %b[70], %b[66], %r35, %b[70] stqp,5 %r33, %b[4], %b[71] }
Теоретическая скорость: 64 комплексных числа за 16 тактов (64/16) = 32 Байт/такт
Замеры скорости

Видим сильное ускорение.
При попытке чтения в 64 потока получается резко менее эффективный код.
APB не может читать в 64 потока, поэтому в дополнение к APB компилятор вставляет обычные операции чтения ldqp. В итоге скорость резко проседает.
Но можно сделать чтение в 32 потока. Для этого напишем чтение в 64 потока и обработаем сначала одну половину строк в одном цикле, а потом вторую половину строк во втором цикле. В каждом цикле будут использованы 32 потока чтения APB.
6. reverse_radix4_vector4_stream32
Чтение в 32 потока из разных мест памяти, запись в одно место памяти.
Код на Си
void reverse_radix4_vector4_stream32(int bit_count_in, myComplex *data_in, myComplex4 *data_out) { int bit_count_out = bit_count_in - 2; int count_in = 1 << bit_count_in; int count_out = 1 << bit_count_out; int shift_out = 64 - bit_count_out; __v2di *in_000 = (__v2di*)&data_in[ 0 * count_in/64]; __v2di *in_001 = (__v2di*)&data_in[ 1 * count_in/64]; __v2di *in_002 = (__v2di*)&data_in[ 2 * count_in/64]; __v2di *in_003 = (__v2di*)&data_in[ 3 * count_in/64]; __v2di *in_010 = (__v2di*)&data_in[ 4 * count_in/64]; __v2di *in_011 = (__v2di*)&data_in[ 5 * count_in/64]; __v2di *in_012 = (__v2di*)&data_in[ 6 * count_in/64]; __v2di *in_013 = (__v2di*)&data_in[ 7 * count_in/64]; __v2di *in_020 = (__v2di*)&data_in[ 8 * count_in/64]; __v2di *in_021 = (__v2di*)&data_in[ 9 * count_in/64]; __v2di *in_022 = (__v2di*)&data_in[10 * count_in/64]; __v2di *in_023 = (__v2di*)&data_in[11 * count_in/64]; __v2di *in_030 = (__v2di*)&data_in[12 * count_in/64]; __v2di *in_031 = (__v2di*)&data_in[13 * count_in/64]; __v2di *in_032 = (__v2di*)&data_in[14 * count_in/64]; __v2di *in_033 = (__v2di*)&data_in[15 * count_in/64]; __v2di *in_100 = (__v2di*)&data_in[16 * count_in/64]; __v2di *in_101 = (__v2di*)&data_in[17 * count_in/64]; __v2di *in_102 = (__v2di*)&data_in[18 * count_in/64]; __v2di *in_103 = (__v2di*)&data_in[19 * count_in/64]; __v2di *in_110 = (__v2di*)&data_in[20 * count_in/64]; __v2di *in_111 = (__v2di*)&data_in[21 * count_in/64]; __v2di *in_112 = (__v2di*)&data_in[22 * count_in/64]; __v2di *in_113 = (__v2di*)&data_in[23 * count_in/64]; __v2di *in_120 = (__v2di*)&data_in[24 * count_in/64]; __v2di *in_121 = (__v2di*)&data_in[25 * count_in/64]; __v2di *in_122 = (__v2di*)&data_in[26 * count_in/64]; __v2di *in_123 = (__v2di*)&data_in[27 * count_in/64]; __v2di *in_130 = (__v2di*)&data_in[28 * count_in/64]; __v2di *in_131 = (__v2di*)&data_in[29 * count_in/64]; __v2di *in_132 = (__v2di*)&data_in[30 * count_in/64]; __v2di *in_133 = (__v2di*)&data_in[31 * count_in/64]; __v2di *in_200 = (__v2di*)&data_in[32 * count_in/64]; __v2di *in_201 = (__v2di*)&data_in[33 * count_in/64]; __v2di *in_202 = (__v2di*)&data_in[34 * count_in/64]; __v2di *in_203 = (__v2di*)&data_in[35 * count_in/64]; __v2di *in_210 = (__v2di*)&data_in[36 * count_in/64]; __v2di *in_211 = (__v2di*)&data_in[37 * count_in/64]; __v2di *in_212 = (__v2di*)&data_in[38 * count_in/64]; __v2di *in_213 = (__v2di*)&data_in[39 * count_in/64]; __v2di *in_220 = (__v2di*)&data_in[40 * count_in/64]; __v2di *in_221 = (__v2di*)&data_in[41 * count_in/64]; __v2di *in_222 = (__v2di*)&data_in[42 * count_in/64]; __v2di *in_223 = (__v2di*)&data_in[43 * count_in/64]; __v2di *in_230 = (__v2di*)&data_in[44 * count_in/64]; __v2di *in_231 = (__v2di*)&data_in[45 * count_in/64]; __v2di *in_232 = (__v2di*)&data_in[46 * count_in/64]; __v2di *in_233 = (__v2di*)&data_in[47 * count_in/64]; __v2di *in_300 = (__v2di*)&data_in[48 * count_in/64]; __v2di *in_301 = (__v2di*)&data_in[49 * count_in/64]; __v2di *in_302 = (__v2di*)&data_in[50 * count_in/64]; __v2di *in_303 = (__v2di*)&data_in[51 * count_in/64]; __v2di *in_310 = (__v2di*)&data_in[52 * count_in/64]; __v2di *in_311 = (__v2di*)&data_in[53 * count_in/64]; __v2di *in_312 = (__v2di*)&data_in[54 * count_in/64]; __v2di *in_313 = (__v2di*)&data_in[55 * count_in/64]; __v2di *in_320 = (__v2di*)&data_in[56 * count_in/64]; __v2di *in_321 = (__v2di*)&data_in[57 * count_in/64]; __v2di *in_322 = (__v2di*)&data_in[58 * count_in/64]; __v2di *in_323 = (__v2di*)&data_in[59 * count_in/64]; __v2di *in_330 = (__v2di*)&data_in[60 * count_in/64]; __v2di *in_331 = (__v2di*)&data_in[61 * count_in/64]; __v2di *in_332 = (__v2di*)&data_in[62 * count_in/64]; __v2di *in_333 = (__v2di*)&data_in[63 * count_in/64]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/32/2; ++i) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 32 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)data_out + offset + 0*16) = __builtin_e2k_qppermb(in_000[2*i+1], in_000[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 1*16) = __builtin_e2k_qppermb(in_000[2*i+1], in_000[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 2*16) = __builtin_e2k_qppermb(in_100[2*i+1], in_100[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 3*16) = __builtin_e2k_qppermb(in_100[2*i+1], in_100[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 4*16) = __builtin_e2k_qppermb(in_200[2*i+1], in_200[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 5*16) = __builtin_e2k_qppermb(in_200[2*i+1], in_200[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 6*16) = __builtin_e2k_qppermb(in_300[2*i+1], in_300[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 7*16) = __builtin_e2k_qppermb(in_300[2*i+1], in_300[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 8*16) = __builtin_e2k_qppermb(in_010[2*i+1], in_010[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 9*16) = __builtin_e2k_qppermb(in_010[2*i+1], in_010[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 10*16) = __builtin_e2k_qppermb(in_110[2*i+1], in_110[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 11*16) = __builtin_e2k_qppermb(in_110[2*i+1], in_110[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 12*16) = __builtin_e2k_qppermb(in_210[2*i+1], in_210[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 13*16) = __builtin_e2k_qppermb(in_210[2*i+1], in_210[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 14*16) = __builtin_e2k_qppermb(in_310[2*i+1], in_310[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 15*16) = __builtin_e2k_qppermb(in_310[2*i+1], in_310[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 16*16) = __builtin_e2k_qppermb(in_020[2*i+1], in_020[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 17*16) = __builtin_e2k_qppermb(in_020[2*i+1], in_020[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 18*16) = __builtin_e2k_qppermb(in_120[2*i+1], in_120[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 19*16) = __builtin_e2k_qppermb(in_120[2*i+1], in_120[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 20*16) = __builtin_e2k_qppermb(in_220[2*i+1], in_220[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 21*16) = __builtin_e2k_qppermb(in_220[2*i+1], in_220[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 22*16) = __builtin_e2k_qppermb(in_320[2*i+1], in_320[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 23*16) = __builtin_e2k_qppermb(in_320[2*i+1], in_320[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 24*16) = __builtin_e2k_qppermb(in_030[2*i+1], in_030[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 25*16) = __builtin_e2k_qppermb(in_030[2*i+1], in_030[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 26*16) = __builtin_e2k_qppermb(in_130[2*i+1], in_130[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 27*16) = __builtin_e2k_qppermb(in_130[2*i+1], in_130[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 28*16) = __builtin_e2k_qppermb(in_230[2*i+1], in_230[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 29*16) = __builtin_e2k_qppermb(in_230[2*i+1], in_230[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 30*16) = __builtin_e2k_qppermb(in_330[2*i+1], in_330[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 31*16) = __builtin_e2k_qppermb(in_330[2*i+1], in_330[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 32*16) = __builtin_e2k_qppermb(in_001[2*i+1], in_001[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 33*16) = __builtin_e2k_qppermb(in_001[2*i+1], in_001[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 34*16) = __builtin_e2k_qppermb(in_101[2*i+1], in_101[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 35*16) = __builtin_e2k_qppermb(in_101[2*i+1], in_101[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 36*16) = __builtin_e2k_qppermb(in_201[2*i+1], in_201[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 37*16) = __builtin_e2k_qppermb(in_201[2*i+1], in_201[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 38*16) = __builtin_e2k_qppermb(in_301[2*i+1], in_301[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 39*16) = __builtin_e2k_qppermb(in_301[2*i+1], in_301[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 40*16) = __builtin_e2k_qppermb(in_011[2*i+1], in_011[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 41*16) = __builtin_e2k_qppermb(in_011[2*i+1], in_011[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 42*16) = __builtin_e2k_qppermb(in_111[2*i+1], in_111[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 43*16) = __builtin_e2k_qppermb(in_111[2*i+1], in_111[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 44*16) = __builtin_e2k_qppermb(in_211[2*i+1], in_211[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 45*16) = __builtin_e2k_qppermb(in_211[2*i+1], in_211[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 46*16) = __builtin_e2k_qppermb(in_311[2*i+1], in_311[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 47*16) = __builtin_e2k_qppermb(in_311[2*i+1], in_311[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 48*16) = __builtin_e2k_qppermb(in_021[2*i+1], in_021[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 49*16) = __builtin_e2k_qppermb(in_021[2*i+1], in_021[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 50*16) = __builtin_e2k_qppermb(in_121[2*i+1], in_121[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 51*16) = __builtin_e2k_qppermb(in_121[2*i+1], in_121[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 52*16) = __builtin_e2k_qppermb(in_221[2*i+1], in_221[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 53*16) = __builtin_e2k_qppermb(in_221[2*i+1], in_221[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 54*16) = __builtin_e2k_qppermb(in_321[2*i+1], in_321[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 55*16) = __builtin_e2k_qppermb(in_321[2*i+1], in_321[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 56*16) = __builtin_e2k_qppermb(in_031[2*i+1], in_031[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 57*16) = __builtin_e2k_qppermb(in_031[2*i+1], in_031[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 58*16) = __builtin_e2k_qppermb(in_131[2*i+1], in_131[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 59*16) = __builtin_e2k_qppermb(in_131[2*i+1], in_131[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 60*16) = __builtin_e2k_qppermb(in_231[2*i+1], in_231[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 61*16) = __builtin_e2k_qppermb(in_231[2*i+1], in_231[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 62*16) = __builtin_e2k_qppermb(in_331[2*i+1], in_331[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 63*16) = __builtin_e2k_qppermb(in_331[2*i+1], in_331[2*i+0], mask_imag); } #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < count_out/32/2; ++i) { uint64_t rev = __builtin_e2k_bitrevd(i); int64_t offset = 32 * (((rev>>(shift_out-1)) & 0xAAAAAAAAAAAAAAAA) | ((rev>>(shift_out+1)) & 0x5555555555555555)); __v2di mask_real = {0x0B0A090803020100, 0x1B1A191813121110}; __v2di mask_imag = {0x0F0E0D0C07060504, 0x1F1E1D1C17161514}; *(__v2du*)((void*)data_out + offset + 64*16) = __builtin_e2k_qppermb(in_002[2*i+1], in_002[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 65*16) = __builtin_e2k_qppermb(in_002[2*i+1], in_002[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 66*16) = __builtin_e2k_qppermb(in_102[2*i+1], in_102[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 67*16) = __builtin_e2k_qppermb(in_102[2*i+1], in_102[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 68*16) = __builtin_e2k_qppermb(in_202[2*i+1], in_202[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 69*16) = __builtin_e2k_qppermb(in_202[2*i+1], in_202[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 70*16) = __builtin_e2k_qppermb(in_302[2*i+1], in_302[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 71*16) = __builtin_e2k_qppermb(in_302[2*i+1], in_302[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 72*16) = __builtin_e2k_qppermb(in_012[2*i+1], in_012[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 73*16) = __builtin_e2k_qppermb(in_012[2*i+1], in_012[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 74*16) = __builtin_e2k_qppermb(in_112[2*i+1], in_112[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 75*16) = __builtin_e2k_qppermb(in_112[2*i+1], in_112[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 76*16) = __builtin_e2k_qppermb(in_212[2*i+1], in_212[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 77*16) = __builtin_e2k_qppermb(in_212[2*i+1], in_212[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 78*16) = __builtin_e2k_qppermb(in_312[2*i+1], in_312[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 79*16) = __builtin_e2k_qppermb(in_312[2*i+1], in_312[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 80*16) = __builtin_e2k_qppermb(in_022[2*i+1], in_022[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 81*16) = __builtin_e2k_qppermb(in_022[2*i+1], in_022[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 82*16) = __builtin_e2k_qppermb(in_122[2*i+1], in_122[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 83*16) = __builtin_e2k_qppermb(in_122[2*i+1], in_122[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 84*16) = __builtin_e2k_qppermb(in_222[2*i+1], in_222[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 85*16) = __builtin_e2k_qppermb(in_222[2*i+1], in_222[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 86*16) = __builtin_e2k_qppermb(in_322[2*i+1], in_322[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 87*16) = __builtin_e2k_qppermb(in_322[2*i+1], in_322[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 88*16) = __builtin_e2k_qppermb(in_032[2*i+1], in_032[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 89*16) = __builtin_e2k_qppermb(in_032[2*i+1], in_032[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 90*16) = __builtin_e2k_qppermb(in_132[2*i+1], in_132[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 91*16) = __builtin_e2k_qppermb(in_132[2*i+1], in_132[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 92*16) = __builtin_e2k_qppermb(in_232[2*i+1], in_232[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 93*16) = __builtin_e2k_qppermb(in_232[2*i+1], in_232[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 94*16) = __builtin_e2k_qppermb(in_332[2*i+1], in_332[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 95*16) = __builtin_e2k_qppermb(in_332[2*i+1], in_332[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 96*16) = __builtin_e2k_qppermb(in_003[2*i+1], in_003[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 97*16) = __builtin_e2k_qppermb(in_003[2*i+1], in_003[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 98*16) = __builtin_e2k_qppermb(in_103[2*i+1], in_103[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 99*16) = __builtin_e2k_qppermb(in_103[2*i+1], in_103[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 100*16) = __builtin_e2k_qppermb(in_203[2*i+1], in_203[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 101*16) = __builtin_e2k_qppermb(in_203[2*i+1], in_203[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 102*16) = __builtin_e2k_qppermb(in_303[2*i+1], in_303[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 103*16) = __builtin_e2k_qppermb(in_303[2*i+1], in_303[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 104*16) = __builtin_e2k_qppermb(in_013[2*i+1], in_013[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 105*16) = __builtin_e2k_qppermb(in_013[2*i+1], in_013[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 106*16) = __builtin_e2k_qppermb(in_113[2*i+1], in_113[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 107*16) = __builtin_e2k_qppermb(in_113[2*i+1], in_113[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 108*16) = __builtin_e2k_qppermb(in_213[2*i+1], in_213[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 109*16) = __builtin_e2k_qppermb(in_213[2*i+1], in_213[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 110*16) = __builtin_e2k_qppermb(in_313[2*i+1], in_313[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 111*16) = __builtin_e2k_qppermb(in_313[2*i+1], in_313[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 112*16) = __builtin_e2k_qppermb(in_023[2*i+1], in_023[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 113*16) = __builtin_e2k_qppermb(in_023[2*i+1], in_023[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 114*16) = __builtin_e2k_qppermb(in_123[2*i+1], in_123[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 115*16) = __builtin_e2k_qppermb(in_123[2*i+1], in_123[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 116*16) = __builtin_e2k_qppermb(in_223[2*i+1], in_223[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 117*16) = __builtin_e2k_qppermb(in_223[2*i+1], in_223[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 118*16) = __builtin_e2k_qppermb(in_323[2*i+1], in_323[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 119*16) = __builtin_e2k_qppermb(in_323[2*i+1], in_323[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 120*16) = __builtin_e2k_qppermb(in_033[2*i+1], in_033[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 121*16) = __builtin_e2k_qppermb(in_033[2*i+1], in_033[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 122*16) = __builtin_e2k_qppermb(in_133[2*i+1], in_133[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 123*16) = __builtin_e2k_qppermb(in_133[2*i+1], in_133[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 124*16) = __builtin_e2k_qppermb(in_233[2*i+1], in_233[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 125*16) = __builtin_e2k_qppermb(in_233[2*i+1], in_233[2*i+0], mask_imag); *(__v2du*)((void*)data_out + offset + 126*16) = __builtin_e2k_qppermb(in_333[2*i+1], in_333[2*i+0], mask_real); *(__v2du*)((void*)data_out + offset + 127*16) = __builtin_e2k_qppermb(in_333[2*i+1], in_333[2*i+0], mask_imag); } }
Основной цикл на ассемблере
.L15871: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=17, incr=1, ind=0, asz=1, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=16, incr=1, ind=0, asz=1, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=15, incr=1, ind=0, asz=1, abs=2, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=14, incr=1, ind=0, asz=1, abs=2, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=13, incr=1, ind=0, asz=1, abs=4, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=12, incr=1, ind=0, asz=1, abs=4, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=11, incr=1, ind=0, asz=1, abs=6, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=10, incr=1, ind=0, asz=1, abs=6, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=9, incr=1, ind=0, asz=1, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=8, incr=1, ind=0, asz=1, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=7, incr=1, ind=0, asz=1, abs=10, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=6, incr=1, ind=0, asz=1, abs=10, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=5, incr=1, ind=0, asz=1, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=4, incr=1, ind=0, asz=1, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=3, incr=1, ind=0, asz=1, abs=14, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=2, incr=1, ind=0, asz=1, abs=14, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=1, incr=1, ind=0, asz=1, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=15, asz=1, abs=16, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=14, asz=1, abs=18, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=13, asz=1, abs=18, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=12, asz=1, abs=20, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=11, asz=1, abs=20, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=10, asz=1, abs=22, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=9, asz=1, abs=22, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=8, asz=1, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=7, asz=1, abs=24, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=6, asz=1, abs=26, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=5, asz=1, abs=26, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=4, asz=1, abs=28, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=3, asz=1, abs=28, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=2, asz=1, abs=30, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=30, disp=0 } .L15388: { loop_mode disp %ctpr1, .L15388 bitrevd,0 %r0, %g16 addd,1 %r0, 0x1, %r0 movaqp,0 area=0, ind=16, am=1, be=0, %g18 movaqp,1 area=0, ind=0, am=0, be=0, %g17 movaqp,2 area=0, ind=16, am=1, be=0, %g20 movaqp,3 area=0, ind=0, am=0, be=0, %g19 } { loop_mode disp %ctpr3, .L13219 shrd,0 %g16, %r7, %g25 shrd,1 %g16, %r6, %g16 movaqp,0 area=1, ind=16, am=1, be=0, %g22 movaqp,1 area=1, ind=0, am=0, be=0, %g21 movaqp,2 area=1, ind=16, am=1, be=0, %g24 movaqp,3 area=1, ind=0, am=0, be=0, %g23 } { loop_mode andd,0 %g25, _f64,_lts0 0xaaaaaaaaaaaaaaaa, %g25 andd,1 %g16, _f64,_lts2 0x5555555555555555, %g16 movaqp,0 area=2, ind=16, am=1, be=0, %g27 movaqp,1 area=2, ind=0, am=0, be=0, %g26 movaqp,2 area=2, ind=16, am=1, be=0, %g29 movaqp,3 area=2, ind=0, am=0, be=0, %g28 } { loop_mode ord,0 %g25, %g16, %g16 movaqp,0 area=3, ind=16, am=1, be=0, %g31 movaqp,1 area=3, ind=0, am=0, be=0, %g30 movaqp,2 area=3, ind=16, am=1, be=0, %r9 movaqp,3 area=3, ind=0, am=0, be=0, %r1 } { loop_mode shld,0 %g16, 0x5, %g16 movaqp,0 area=4, ind=16, am=1, be=0, %r10 movaqp,1 area=4, ind=0, am=0, be=0, %g25 movaqp,2 area=4, ind=16, am=1, be=0, %r12 movaqp,3 area=4, ind=0, am=0, be=0, %r11 } { loop_mode qppermb,0 %g18, %g17, %r4, %r17 qppermb,1 %g18, %g17, %r5, %g17 addd,2 %r2, %g16, %g16 qppermb,3 %g20, %g19, %r4, %g18 qppermb,4 %g20, %g19, %r5, %g19 movaqp,0 area=5, ind=16, am=1, be=0, %r14 movaqp,1 area=5, ind=0, am=0, be=0, %r13 movaqp,2 area=5, ind=16, am=1, be=0, %r16 movaqp,3 area=5, ind=0, am=0, be=0, %r15 } { loop_mode qppermb,0 %g22, %g21, %r4, %r21 qppermb,1 %g22, %g21, %r5, %g21 stqp,2 %g16, _f16s,_lts0lo 0x410, %g17 qppermb,3 %g24, %g23, %r4, %g22 qppermb,4 %g24, %g23, %r5, %g23 movaqp,0 area=6, ind=16, am=1, be=0, %r18 movaqp,1 area=6, ind=0, am=0, be=0, %g20 movaqp,2 area=6, ind=16, am=1, be=0, %r20 movaqp,3 area=6, ind=0, am=0, be=0, %r19 } { loop_mode qppermb,0 %g27, %g26, %r4, %r24 qppermb,1 %g27, %g26, %r5, %g26 stqp,2 %g16, _f16s,_lts0lo 0x400, %r17 qppermb,3 %g29, %g28, %r4, %g27 qppermb,4 %g29, %g28, %r5, %g28 stqp,5 %g16, _f16s,_lts0hi 0x430, %g19 movaqp,0 area=7, ind=16, am=1, be=0, %g24 movaqp,1 area=7, ind=0, am=0, be=0, %g17 movaqp,2 area=7, ind=16, am=1, be=0, %r23 movaqp,3 area=7, ind=0, am=0, be=0, %r22 } { loop_mode qppermb,0 %g31, %g30, %r4, %r26 qppermb,1 %g31, %g30, %r5, %g30 stqp,2 %g16, _f16s,_lts0lo 0x420, %g18 qppermb,3 %r9, %r1, %r4, %g31 qppermb,4 %r9, %r1, %r5, %r1 stqp,5 %g16, _f16s,_lts0hi 0x470, %g23 movaqp,0 area=8, ind=16, am=1, be=0, %g29 movaqp,1 area=8, ind=0, am=0, be=0, %g19 movaqp,2 area=8, ind=16, am=1, be=0, %r25 movaqp,3 area=8, ind=0, am=0, be=0, %r17 } { loop_mode qppermb,0 %r10, %g25, %r4, %r28 qppermb,1 %r10, %g25, %r5, %g25 stqp,2 %g16, _f16s,_lts0lo 0x440, %r21 qppermb,3 %r12, %r11, %r4, %r10 qppermb,4 %r12, %r11, %r5, %r11 stqp,5 %g16, _f16s,_lts0hi 0x450, %g21 movaqp,0 area=9, ind=16, am=1, be=0, %g23 movaqp,1 area=9, ind=0, am=0, be=0, %g18 movaqp,2 area=9, ind=16, am=1, be=0, %r27 movaqp,3 area=9, ind=0, am=0, be=0, %r9 } { loop_mode qppermb,0 %r14, %r13, %r4, %r30 qppermb,1 %r14, %r13, %r5, %r13 stqp,2 %g16, _f16s,_lts0lo 0x460, %g22 qppermb,3 %r16, %r15, %r4, %r14 qppermb,4 %r16, %r15, %r5, %r15 stqp,5 %g16, _f16s,_lts0hi 0x490, %g26 movaqp,0 area=10, ind=16, am=1, be=0, %r12 movaqp,1 area=10, ind=0, am=0, be=0, %g21 movaqp,2 area=10, ind=16, am=1, be=0, %r29 movaqp,3 area=10, ind=0, am=0, be=0, %r21 } { loop_mode qppermb,0 %r18, %g20, %r4, %r32 qppermb,1 %r18, %g20, %r5, %g20 stqp,2 %g16, _f16s,_lts0lo 0x480, %r24 qppermb,3 %r20, %r19, %r4, %r18 qppermb,4 %r20, %r19, %r5, %r19 stqp,5 %g16, _f16s,_lts0hi 0x4b0, %g28 movaqp,0 area=11, ind=16, am=1, be=0, %g26 movaqp,1 area=11, ind=0, am=0, be=0, %g22 movaqp,2 area=11, ind=16, am=1, be=0, %r31 movaqp,3 area=11, ind=0, am=0, be=0, %r16 } { loop_mode qppermb,0 %g24, %g17, %r4, %r34 qppermb,1 %g24, %g17, %r5, %g17 stqp,2 %g16, _f16s,_lts0lo 0x4a0, %g27 qppermb,3 %r23, %r22, %r4, %g24 qppermb,4 %r23, %r22, %r5, %r22 stqp,5 %g16, _f16s,_lts0hi 0x4d0, %g30 movaqp,0 area=12, ind=16, am=1, be=0, %r20 movaqp,1 area=12, ind=0, am=0, be=0, %g28 movaqp,2 area=12, ind=16, am=1, be=0, %r33 movaqp,3 area=12, ind=0, am=0, be=0, %r24 } { loop_mode qppermb,0 %g29, %g19, %r4, %r36 qppermb,1 %g29, %g19, %r5, %g19 stqp,2 %g16, _f16s,_lts0lo 0x4c0, %r26 qppermb,3 %r25, %r17, %r4, %g29 qppermb,4 %r25, %r17, %r5, %r17 stqp,5 %g16, _f16s,_lts0hi 0x4f0, %r1 movaqp,0 area=13, ind=16, am=1, be=0, %g30 movaqp,1 area=13, ind=0, am=0, be=0, %g27 movaqp,2 area=13, ind=16, am=1, be=0, %r35 movaqp,3 area=13, ind=0, am=0, be=0, %r23 } { loop_mode qppermb,0 %g23, %g18, %r4, %r38 qppermb,1 %g23, %g18, %r5, %g18 stqp,2 %g16, _f16s,_lts0lo 0x4e0, %g31 qppermb,3 %r27, %r9, %r4, %g23 qppermb,4 %r27, %r9, %r5, %r9 stqp,5 %g16, _f16s,_lts0hi 0x510, %g25 movaqp,0 area=14, ind=16, am=1, be=0, %r25 movaqp,1 area=14, ind=0, am=0, be=0, %r1 movaqp,2 area=14, ind=16, am=1, be=0, %r37 movaqp,3 area=14, ind=0, am=0, be=0, %r26 } { loop_mode qppermb,0 %r12, %g21, %r4, %r40 qppermb,1 %r12, %g21, %r5, %g21 stqp,2 %g16, _f16s,_lts0lo 0x500, %r28 qppermb,3 %r29, %r21, %r4, %r12 qppermb,4 %r29, %r21, %r5, %r21 stqp,5 %g16, _f16s,_lts0hi 0x530, %r11 movaqp,0 area=15, ind=16, am=1, be=0, %g31 movaqp,1 area=15, ind=0, am=0, be=0, %g25 movaqp,2 area=15, ind=16, am=1, be=0, %r39 movaqp,3 area=15, ind=0, am=0, be=0, %r27 } { loop_mode qppermb,0 %g26, %g22, %r4, %r11 qppermb,1 %g26, %g22, %r5, %g22 stqp,2 %g16, _f16s,_lts0lo 0x520, %r10 qppermb,3 %r31, %r16, %r4, %g26 qppermb,4 %r31, %r16, %r5, %r16 stqp,5 %g16, _f16s,_lts0hi 0x550, %r13 } { loop_mode qppermb,0 %r20, %g28, %r4, %r10 qppermb,1 %r20, %g28, %r5, %g28 stqp,2 %g16, _f16s,_lts0lo 0x540, %r30 qppermb,3 %r33, %r24, %r4, %r13 qppermb,4 %r33, %r24, %r5, %r20 stqp,5 %g16, _f16s,_lts0hi 0x570, %r15 } { loop_mode qppermb,0 %g30, %g27, %r4, %r15 qppermb,1 %g30, %g27, %r5, %g27 stqp,2 %g16, _f16s,_lts0lo 0x560, %r14 qppermb,3 %r35, %r23, %r4, %g30 qppermb,4 %r35, %r23, %r5, %r23 stqp,5 %g16, _f16s,_lts0hi 0x590, %g20 } { loop_mode qppermb,0 %r25, %r1, %r4, %g20 qppermb,1 %r25, %r1, %r5, %r1 stqp,2 %g16, _f16s,_lts0lo 0x580, %r32 qppermb,3 %r37, %r26, %r4, %r14 qppermb,4 %r37, %r26, %r5, %r24 stqp,5 %g16, _f16s,_lts0hi 0x5b0, %r19 } { loop_mode qppermb,0 %g31, %g25, %r4, %r19 qppermb,1 %g31, %g25, %r5, %g25 stqp,2 %g16, _f16s,_lts0lo 0x5a0, %r18 qppermb,3 %r39, %r27, %r4, %g31 qppermb,4 %r39, %r27, %r5, %r25 stqp,5 %g16, _f16s,_lts0hi 0x5d0, %g17 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x5c0, %r34 stqp,5 %g16, _f16s,_lts0hi 0x5f0, %r22 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x5e0, %g24 stqp,5 %g16, _f16s,_lts0hi 0x610, %g19 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x600, %r36 stqp,5 %g16, _f16s,_lts0hi 0x630, %r17 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x620, %g29 stqp,5 %g16, _f16s,_lts0hi 0x650, %g18 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x640, %r38 stqp,5 %g16, _f16s,_lts0hi 0x670, %r9 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x660, %g23 stqp,5 %g16, _f16s,_lts0hi 0x690, %g21 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x680, %r40 stqp,5 %g16, _f16s,_lts0hi 0x6b0, %r21 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x6a0, %r12 stqp,5 %g16, _f16s,_lts0hi 0x6d0, %g22 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x6c0, %r11 stqp,5 %g16, _f16s,_lts0hi 0x6f0, %r16 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x6e0, %g26 stqp,5 %g16, _f16s,_lts0hi 0x710, %g28 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x700, %r10 stqp,5 %g16, _f16s,_lts0hi 0x730, %r20 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x720, %r13 stqp,5 %g16, _f16s,_lts0hi 0x750, %g27 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x740, %r15 stqp,5 %g16, _f16s,_lts0hi 0x770, %r23 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x760, %g30 stqp,5 %g16, _f16s,_lts0hi 0x790, %r1 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x780, %g20 stqp,5 %g16, _f16s,_lts0hi 0x7b0, %r24 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x7a0, %r14 stqp,5 %g16, _f16s,_lts0hi 0x7d0, %g25 } { loop_mode stqp,2 %g16, _f16s,_lts0lo 0x7c0, %r19 stqp,5 %g16, _f16s,_lts0hi 0x7f0, %r25 } { loop_mode ct %ctpr1 ? %NOT_LOOP_END alc alcf=0, alct=1 stqp,2 %g16, _f16s,_lts0lo 0x7e0, %g31 } { loop_mode ct %ctpr3 alc alcf=0, alct=1 } .L16599: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=17, incr=1, ind=0, asz=1, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=16, incr=1, ind=0, asz=1, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=15, incr=1, ind=0, asz=1, abs=2, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=14, incr=1, ind=0, asz=1, abs=2, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=13, incr=1, ind=0, asz=1, abs=4, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=12, incr=1, ind=0, asz=1, abs=4, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=11, incr=1, ind=0, asz=1, abs=6, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=10, incr=1, ind=0, asz=1, abs=6, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=9, incr=1, ind=0, asz=1, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=8, incr=1, ind=0, asz=1, abs=8, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=7, incr=1, ind=0, asz=1, abs=10, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=6, incr=1, ind=0, asz=1, abs=10, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=5, incr=1, ind=0, asz=1, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=4, incr=1, ind=0, asz=1, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=3, incr=1, ind=0, asz=1, abs=14, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=2, incr=1, ind=0, asz=1, abs=14, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=1, incr=1, ind=0, asz=1, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=15, asz=1, abs=16, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=14, asz=1, abs=18, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=13, asz=1, abs=18, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=12, asz=1, abs=20, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=11, asz=1, abs=20, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=10, asz=1, abs=22, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=9, asz=1, abs=22, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=8, asz=1, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=7, asz=1, abs=24, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=6, asz=1, abs=26, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=5, asz=1, abs=26, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=4, asz=1, abs=28, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=3, asz=1, abs=28, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=2, asz=1, abs=30, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=30, disp=0 } .L15607: { loop_mode disp %ctpr1, .L15607 bitrevd,0 %r0, %g16 addd,1 %r0, 0x1, %r0 movaqp,0 area=0, ind=16, am=1, be=0, %g18 movaqp,1 area=0, ind=0, am=0, be=0, %g17 movaqp,2 area=0, ind=16, am=1, be=0, %g20 movaqp,3 area=0, ind=0, am=0, be=0, %g19 } { loop_mode disp %ctpr3, .L15094 shrd,0 %g16, %r7, %g25 shrd,1 %g16, %r6, %g16 movaqp,0 area=1, ind=16, am=1, be=0, %g22 movaqp,1 area=1, ind=0, am=0, be=0, %g21 movaqp,2 area=1, ind=16, am=1, be=0, %g24 movaqp,3 area=1, ind=0, am=0, be=0, %g23 } { loop_mode andd,0 %g25, _f64,_lts0 0xaaaaaaaaaaaaaaaa, %g25 andd,1 %g16, _f64,_lts2 0x5555555555555555, %g16 movaqp,0 area=2, ind=16, am=1, be=0, %g27 movaqp,1 area=2, ind=0, am=0, be=0, %g26 movaqp,2 area=2, ind=16, am=1, be=0, %g29 movaqp,3 area=2, ind=0, am=0, be=0, %g28 } { loop_mode ord,0 %g25, %g16, %g16 movaqp,0 area=3, ind=16, am=1, be=0, %g31 movaqp,1 area=3, ind=0, am=0, be=0, %g30 movaqp,2 area=3, ind=16, am=1, be=0, %r14 movaqp,3 area=3, ind=0, am=0, be=0, %r11 } { loop_mode shld,0 %g16, 0x5, %g16 movaqp,0 area=4, ind=16, am=1, be=0, %r18 movaqp,1 area=4, ind=0, am=0, be=0, %g25 movaqp,2 area=4, ind=16, am=1, be=0, %r20 movaqp,3 area=4, ind=0, am=0, be=0, %r19 } { loop_mode qppermb,0 %g18, %g17, %r4, %r31 qppermb,1 %g18, %g17, %r5, %g17 addd,2 %r2, %g16, %g20 qppermb,3 %g20, %g19, %r4, %g18 qppermb,4 %g20, %g19, %r5, %g19 movaqp,0 area=5, ind=16, am=1, be=0, %r25 movaqp,1 area=5, ind=0, am=0, be=0, %r23 movaqp,2 area=5, ind=16, am=1, be=0, %r29 movaqp,3 area=5, ind=0, am=0, be=0, %r28 } { loop_mode qppermb,0 %g22, %g21, %r4, %r50 qppermb,1 %g22, %g21, %r5, %g21 stqp,2 %r2, %g16, %r31 qppermb,3 %g24, %g23, %r4, %g22 qppermb,4 %g24, %g23, %r5, %g23 movaqp,0 area=6, ind=16, am=1, be=0, %r37 movaqp,1 area=6, ind=0, am=0, be=0, %r34 movaqp,2 area=6, ind=16, am=1, be=0, %r46 movaqp,3 area=6, ind=0, am=0, be=0, %r39 } { loop_mode qppermb,0 %g27, %g26, %r4, %r56 qppermb,1 %g27, %g26, %r5, %g26 stqp,2 0x10, %g20, %g17 qppermb,3 %g29, %g28, %r4, %g27 qppermb,4 %g29, %g28, %r5, %g28 stqp,5 %g20, _f16s,_lts0lo 0x30, %g19 movaqp,0 area=7, ind=16, am=1, be=0, %g24 movaqp,1 area=7, ind=0, am=0, be=0, %g16 movaqp,2 area=7, ind=16, am=1, be=0, %r53 movaqp,3 area=7, ind=0, am=0, be=0, %r31 } { loop_mode qppermb,0 %g31, %g30, %r4, %r58 qppermb,1 %g31, %g30, %r5, %g30 stqp,2 %g20, _f16s,_lts0lo 0x20, %g18 qppermb,3 %r14, %r11, %r4, %g31 qppermb,4 %r14, %r11, %r5, %r11 stqp,5 %g20, _f16s,_lts0hi 0x70, %g23 movaqp,0 area=8, ind=16, am=1, be=0, %g19 movaqp,1 area=8, ind=0, am=0, be=0, %g17 movaqp,2 area=8, ind=16, am=1, be=0, %r57 movaqp,3 area=8, ind=0, am=0, be=0, %g29 } { loop_mode qppermb,0 %r18, %g25, %r4, %r61 qppermb,1 %r18, %g25, %r5, %g25 stqp,2 %g20, _f16s,_lts0lo 0x40, %r50 qppermb,3 %r20, %r19, %r4, %r18 qppermb,4 %r20, %r19, %r5, %r19 stqp,5 %g20, _f16s,_lts0hi 0x50, %g21 movaqp,0 area=9, ind=16, am=1, be=0, %g23 movaqp,1 area=9, ind=0, am=0, be=0, %g18 movaqp,2 area=9, ind=16, am=1, be=0, %r59 movaqp,3 area=9, ind=0, am=0, be=0, %r14 } { loop_mode qppermb,0 %r25, %r23, %r4, %r63 qppermb,1 %r25, %r23, %r5, %r23 stqp,2 %g20, _f16s,_lts0lo 0x60, %g22 qppermb,3 %r29, %r28, %r4, %r25 qppermb,4 %r29, %r28, %r5, %r28 stqp,5 %g20, _f16s,_lts0hi 0x90, %g26 movaqp,0 area=10, ind=16, am=1, be=0, %r20 movaqp,1 area=10, ind=0, am=0, be=0, %g21 movaqp,2 area=10, ind=16, am=1, be=0, %r62 movaqp,3 area=10, ind=0, am=0, be=0, %r50 } { loop_mode qppermb,0 %r37, %r34, %r4, %b[1] qppermb,1 %r37, %r34, %r5, %r34 stqp,2 %g20, _f16s,_lts0lo 0x80, %r56 qppermb,3 %r46, %r39, %r4, %r37 qppermb,4 %r46, %r39, %r5, %r39 stqp,5 %g20, _f16s,_lts0hi 0xb0, %g28 movaqp,0 area=11, ind=16, am=1, be=0, %g26 movaqp,1 area=11, ind=0, am=0, be=0, %g22 movaqp,2 area=11, ind=16, am=1, be=0, %b[0] movaqp,3 area=11, ind=0, am=0, be=0, %r29 } { loop_mode qppermb,0 %g24, %g16, %r4, %b[3] qppermb,1 %g24, %g16, %r5, %g16 stqp,2 %g20, _f16s,_lts0lo 0xa0, %g27 qppermb,3 %r53, %r31, %r4, %g24 qppermb,4 %r53, %r31, %r5, %r31 stqp,5 %g20, _f16s,_lts0hi 0xd0, %g30 movaqp,0 area=12, ind=16, am=1, be=0, %r46 movaqp,1 area=12, ind=0, am=0, be=0, %g28 movaqp,2 area=12, ind=16, am=1, be=0, %b[2] movaqp,3 area=12, ind=0, am=0, be=0, %r56 } { loop_mode qppermb,0 %g19, %g17, %r4, %b[5] qppermb,1 %g19, %g17, %r5, %g17 stqp,2 %g20, _f16s,_lts0lo 0xc0, %r58 qppermb,3 %r57, %g29, %r4, %g19 qppermb,4 %r57, %g29, %r5, %g29 stqp,5 %g20, _f16s,_lts0hi 0xf0, %r11 movaqp,0 area=13, ind=16, am=1, be=0, %g30 movaqp,1 area=13, ind=0, am=0, be=0, %g27 movaqp,2 area=13, ind=16, am=1, be=0, %b[4] movaqp,3 area=13, ind=0, am=0, be=0, %r53 } { loop_mode qppermb,0 %g23, %g18, %r4, %b[8] qppermb,1 %g23, %g18, %r5, %g18 stqp,2 %g20, _f16s,_lts0lo 0xe0, %g31 qppermb,3 %r59, %r14, %r4, %g23 qppermb,4 %r59, %r14, %r5, %r14 stqp,5 %g20, _f16s,_lts0hi 0x110, %g25 movaqp,0 area=14, ind=16, am=1, be=0, %r57 movaqp,1 area=14, ind=0, am=0, be=0, %r11 movaqp,2 area=14, ind=16, am=1, be=0, %b[6] movaqp,3 area=14, ind=0, am=0, be=0, %r58 } { loop_mode qppermb,0 %r20, %g21, %r4, %b[10] qppermb,1 %r20, %g21, %r5, %g21 stqp,2 %g20, _f16s,_lts0lo 0x100, %r61 qppermb,3 %r62, %r50, %r4, %r20 qppermb,4 %r62, %r50, %r5, %r50 stqp,5 %g20, _f16s,_lts0hi 0x130, %r19 movaqp,0 area=15, ind=16, am=1, be=0, %g31 movaqp,1 area=15, ind=0, am=0, be=0, %g25 movaqp,2 area=15, ind=16, am=1, be=0, %b[9] movaqp,3 area=15, ind=0, am=0, be=0, %r59 } { loop_mode qppermb,0 %g26, %g22, %r4, %r19 qppermb,1 %g26, %g22, %r5, %g22 stqp,2 %g20, _f16s,_lts0lo 0x120, %r18 qppermb,3 %b[0], %r29, %r4, %g26 qppermb,4 %b[0], %r29, %r5, %r29 stqp,5 %g20, _f16s,_lts0hi 0x150, %r23 } { loop_mode qppermb,0 %r46, %g28, %r4, %r18 qppermb,1 %r46, %g28, %r5, %g28 stqp,2 %g20, _f16s,_lts0lo 0x140, %r63 qppermb,3 %b[2], %r56, %r4, %r23 qppermb,4 %b[2], %r56, %r5, %r46 stqp,5 %g20, _f16s,_lts0hi 0x170, %r28 } { loop_mode qppermb,0 %g30, %g27, %r4, %r28 qppermb,1 %g30, %g27, %r5, %g27 stqp,2 %g20, _f16s,_lts0lo 0x160, %r25 qppermb,3 %b[4], %r53, %r4, %g30 qppermb,4 %b[4], %r53, %r5, %r53 stqp,5 %g20, _f16s,_lts0hi 0x190, %r34 } { loop_mode qppermb,0 %r57, %r11, %r4, %r25 qppermb,1 %r57, %r11, %r5, %r11 stqp,2 %g20, _f16s,_lts0lo 0x180, %b[1] qppermb,3 %b[6], %r58, %r4, %r34 qppermb,4 %b[6], %r58, %r5, %r56 stqp,5 %g20, _f16s,_lts0hi 0x1b0, %r39 } { loop_mode qppermb,0 %g31, %g25, %r4, %r39 qppermb,1 %g31, %g25, %r5, %g25 stqp,2 %g20, _f16s,_lts0lo 0x1a0, %r37 qppermb,3 %b[9], %r59, %r4, %g31 qppermb,4 %b[9], %r59, %r5, %r57 stqp,5 %g20, _f16s,_lts0hi 0x1d0, %g16 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x1c0, %b[3] stqp,5 %g20, _f16s,_lts0hi 0x1f0, %r31 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x1e0, %g24 stqp,5 %g20, _f16s,_lts0hi 0x210, %g17 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x200, %b[5] stqp,5 %g20, _f16s,_lts0hi 0x230, %g29 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x220, %g19 stqp,5 %g20, _f16s,_lts0hi 0x250, %g18 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x240, %b[8] stqp,5 %g20, _f16s,_lts0hi 0x270, %r14 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x260, %g23 stqp,5 %g20, _f16s,_lts0hi 0x290, %g21 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x280, %b[10] stqp,5 %g20, _f16s,_lts0hi 0x2b0, %r50 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x2a0, %r20 stqp,5 %g20, _f16s,_lts0hi 0x2d0, %g22 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x2c0, %r19 stqp,5 %g20, _f16s,_lts0hi 0x2f0, %r29 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x2e0, %g26 stqp,5 %g20, _f16s,_lts0hi 0x310, %g28 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x300, %r18 stqp,5 %g20, _f16s,_lts0hi 0x330, %r46 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x320, %r23 stqp,5 %g20, _f16s,_lts0hi 0x350, %g27 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x340, %r28 stqp,5 %g20, _f16s,_lts0hi 0x370, %r53 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x360, %g30 stqp,5 %g20, _f16s,_lts0hi 0x390, %r11 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x380, %r25 stqp,5 %g20, _f16s,_lts0hi 0x3b0, %r56 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x3a0, %r34 stqp,5 %g20, _f16s,_lts0hi 0x3d0, %g25 } { loop_mode stqp,2 %g20, _f16s,_lts0lo 0x3c0, %r39 stqp,5 %g20, _f16s,_lts0hi 0x3f0, %r57 } { loop_mode ct %ctpr1 ? %NOT_LOOP_END alc alcf=0, alct=1 stqp,2 %g20, _f16s,_lts0lo 0x3e0, %g31 } { loop_mode ct %ctpr3 alc alcf=0, alct=1 }
Теоретическая скорость: 128 комплексных чисел за 40 тактов (128/40) = 25.6 Байт/такт
Замеры скорости

Видим замедление по всей длине графика.
Итоги по reverse_radix4_vector4


Победителем можно считать reverse_radix4_vector4_stream16.
При реализации Vector-4 Radix-4 FFT будем использовать его.
Пишем векторный Stage
stage_radix2_vector2
1. stage_radix2_vector2_etalon
Эталонный вариант для сравнения на корректность.
Код на Си
void stage_radix2_vector2_etalon(int data_count, myComplex2 *data_in, myComplex2 *data_out, myComplex2 *coef) { myComplex2 *x_in = &data_in[0]; myComplex2 *y_in = &data_in[1]; myComplex2 *c_in = coef; myComplex2 *out_add = &data_out[0]; myComplex2 *out_sub = &data_out[data_count/2]; #pragma ivdep #pragma unroll(1) // #pragma prefetch for(int64_t i = 0; i < data_count/2; ++i) { myComplex2 x = x_in[2*i]; myComplex2 y = y_in[2*i]; myComplex2 c = c_in[i]; myComplex2 cy = complex2_mul(c, y); out_add[i] = complex2_add(x, cy); out_sub[i] = complex2_sub(x, cy); } }
Основной цикл на ассемблере
.L917: { fapb ct=1, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=5, abs=0, disp=0 } .L546: { loop_mode pfmuls,0,sm %b[67], %b[6], %b[37] pfsubs,1,sm %b[46], %b[24], %b[57] staad,2 %b[61], %aad1[ %aasti3 + _f32s,_lts0 0x8 ] pfmul_adds,3,sm %b[55], %b[13], %b[43], %b[14] pfadds,4,sm %b[46], %b[24], %b[58] staad,5 %b[62], %aad2[ %aasti4 + _f32s,_lts0 0x8 ] movad,0 area=0, ind=16, am=0, be=0, %b[0] movad,1 area=0, ind=24, am=0, be=0, %b[1] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END pfmuls,0,sm %b[67], %b[7], %b[62] pfsubs,1,sm %b[35], %b[56], %b[69] staad,2 %b[73], %aad1[ %aasti3 ] incr,2 %aaincr3 pfmul_rsubs,3,sm %b[55], %b[12], %b[68], %b[46] pfadds,4,sm %b[35], %b[56], %b[70] staad,5 %b[74], %aad2[ %aasti4 ] incr,5 %aaincr3 movad,0 area=0, ind=0, am=0, be=0, %b[13] movad,1 area=0, ind=8, am=1, be=0, %b[24] movad,2 area=0, ind=8, am=1, be=0, %b[61] movad,3 area=0, ind=0, am=0, be=0, %b[43] }
Теоретическая скорость: 4 комплексных числа за 2 такта (4/2) = 16 Байт/такт
Замеры скорости

2. stage_radix2_vector2
Прямая векторизация базового алгоритма в 2 раза с помощью инструкций SIMD64.
Код на Си
void stage_radix2_vector2(int data_count, myComplex2 *data_in, myComplex2 *data_out, myComplex2 *coef) { uint64_t *x_real_in = (uint64_t*)&data_in[0].real; uint64_t *x_imag_in = (uint64_t*)&data_in[0].imag; uint64_t *y_real_in = (uint64_t*)&data_in[1].real; uint64_t *y_imag_in = (uint64_t*)&data_in[1].imag; uint64_t *c_real_in = (uint64_t*)&coef[0].real; uint64_t *c_imag_in = (uint64_t*)&coef[0].imag; uint64_t *out_add_real = (uint64_t*)&data_out[0].real; uint64_t *out_add_imag = (uint64_t*)&data_out[0].imag; uint64_t *out_sub_real = (uint64_t*)&data_out[data_count/2].real; uint64_t *out_sub_imag = (uint64_t*)&data_out[data_count/2].imag; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < data_count/2; ++i) { uint64_t x_real = x_real_in[4*i]; uint64_t x_imag = x_imag_in[4*i]; uint64_t y_real = y_real_in[4*i]; uint64_t y_imag = y_imag_in[4*i]; uint64_t c_real = c_real_in[2*i]; uint64_t c_imag = c_imag_in[2*i]; uint64_t cy_real = __builtin_e2k_pfsubs(__builtin_e2k_pfmuls(c_real, y_real), __builtin_e2k_pfmuls(c_imag, y_imag)); uint64_t cy_imag = __builtin_e2k_pfadds(__builtin_e2k_pfmuls(c_real, y_imag), __builtin_e2k_pfmuls(c_imag, y_real)); out_add_real[2*i] = __builtin_e2k_pfadds(x_real, cy_real); out_add_imag[2*i] = __builtin_e2k_pfadds(x_imag, cy_imag); out_sub_real[2*i] = __builtin_e2k_pfsubs(x_real, cy_real); out_sub_imag[2*i] = __builtin_e2k_pfsubs(x_imag, cy_imag); } }
Основной цикл на ассемблере
.L1379: { fapb ct=1, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=5, abs=0, disp=0 } .L1117: { loop_mode pfmuls,0,sm %b[67], %b[6], %b[37] pfsubs,1,sm %b[46], %b[24], %b[57] staad,2 %b[61], %aad1[ %aasti3 + _f32s,_lts0 0x8 ] pfmul_adds,3,sm %b[55], %b[13], %b[43], %b[14] pfadds,4,sm %b[46], %b[24], %b[58] staad,5 %b[62], %aad2[ %aasti4 + _f32s,_lts0 0x8 ] movad,0 area=0, ind=16, am=0, be=0, %b[0] movad,1 area=0, ind=24, am=0, be=0, %b[1] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END pfmuls,0,sm %b[67], %b[7], %b[62] pfsubs,1,sm %b[35], %b[56], %b[69] staad,2 %b[73], %aad1[ %aasti3 ] incr,2 %aaincr3 pfmul_rsubs,3,sm %b[55], %b[12], %b[68], %b[46] pfadds,4,sm %b[35], %b[56], %b[70] staad,5 %b[74], %aad2[ %aasti4 ] incr,5 %aaincr3 movad,0 area=0, ind=0, am=0, be=0, %b[13] movad,1 area=0, ind=8, am=1, be=0, %b[24] movad,2 area=0, ind=8, am=1, be=0, %b[61] movad,3 area=0, ind=0, am=0, be=0, %b[43] }
Код на ассемблере получился такой же, как в эталонном варианте, то есть компилятор самостоятельно векторизовал эталонный вариант в 2 раза.
Теоретическая скорость: 4 комплексных числа за 2 такта (4/2) = 16 Байт/такт
Замеры скорости

Из консольного вывода от разных размеров массива видно, что эталонный вариант всегда выполняется на 40–50 тактов быстрее.
Я не смог понять, с чем это связано (особенности расположения кода в памяти?).
Итоги по stage_radix2_vector2


stage_radix2_vector4
1. stage_radix2_vector4_etalon
Эталонный вариант для сравнения на корректность.
Код на Си
void stage_radix2_vector4_etalon(int data_count, myComplex4 *data_in, myComplex4 *data_out, myComplex4 *coef) { myComplex4 *x_in = &data_in[0]; myComplex4 *y_in = &data_in[1]; myComplex4 *c_in = coef; myComplex4 *out_add = &data_out[0]; myComplex4 *out_sub = &data_out[data_count/2]; #pragma ivdep #pragma unroll(1) // #pragma prefetch for(int64_t i = 0; i < data_count/2; ++i) { myComplex4 x = x_in[2*i]; myComplex4 y = y_in[2*i]; myComplex4 c = c_in[i]; myComplex4 cy = complex4_mul(c, y); out_add[i] = complex4_add(x, cy); out_sub[i] = complex4_sub(x, cy); } }
Основной цикл на ассемблере
.L1531: { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=4, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=4, abs=0, disp=16 } { fapb ct=1, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=4, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=4, abs=16, disp=32 } .L926: { loop_mode pfmuls,0,sm %b[77], %b[39], %b[22] pfsubs,1,sm %b[36], %b[69], %b[52] staad,2 %b[54], %aad1[ %aasti3 + _f32s,_lts0 0x8 ] pfmul_adds,3,sm %b[51], %b[43], %b[72], %b[7] pfadds,4,sm %b[36], %b[69], %b[60] staad,5 %b[62], %aad2[ %aasti4 + _f32s,_lts0 0x8 ] movad,2 area=1, ind=16, am=0, be=0, %b[0] movad,3 area=0, ind=0, am=0, be=0, %b[1] } { loop_mode pfmuls,0,sm %b[5], %b[4], %b[62] pfsubs,1,sm %b[23], %b[63], %b[66] staad,2 %b[68], %aad1[ %aasti3 ] pfmul_adds,3,sm %b[44], %b[8], %b[78], %b[54] pfadds,4,sm %b[23], %b[63], %b[69] staad,5 %b[71], %aad2[ %aasti4 ] movad,0 area=1, ind=0, am=0, be=0, %b[36] movad,1 area=1, ind=8, am=1, be=0, %b[43] movad,3 area=1, ind=8, am=0, be=0, %b[51] } { loop_mode pfmuls,0,sm %b[77], %b[55], %b[68] pfsubs,1,sm %b[35], %b[11], %b[71] staad,2 %b[73], %aad1[ %aasti3 + _f32s,_lts0 0x18 ] pfmul_rsubs,3,sm %b[49], %b[57], %b[24], %b[63] pfadds,4,sm %b[35], %b[11], %b[72] staad,5 %b[74], %aad2[ %aasti4 + _f32s,_lts0 0x18 ] movad,0 area=0, ind=16, am=0, be=0, %b[8] movad,1 area=0, ind=24, am=0, be=0, %b[23] movad,3 area=1, ind=0, am=0, be=0, %b[44] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END pfmuls,0,sm %b[5], %b[48], %b[74] pfsubs,1,sm %b[20], %b[58], %b[77] staad,2 %b[79], %aad1[ %aasti3 + _f32s,_lts0 0x10 ] incr,2 %aaincr3 pfmul_rsubs,3,sm %b[42], %b[50], %b[64], %b[57] pfadds,4,sm %b[20], %b[58], %b[78] staad,5 %b[80], %aad2[ %aasti4 + _f32s,_lts0 0x10 ] incr,5 %aaincr3 movad,0 area=0, ind=0, am=0, be=0, %b[11] movad,1 area=0, ind=8, am=1, be=0, %b[24] movad,2 area=1, ind=24, am=1, be=0, %b[35] movad,3 area=0, ind=8, am=1, be=0, %b[73] }
Теоретическая скорость: 8 комплексных чисел за 4 такта (8/4) = 16 Байт/такт
Замеры скорости

2. stage_radix2_vector4
Прямая векторизация базового алгоритма в 4 раза с помощью инструкций SIMD128.
Код на Си
void stage_radix2_vector4(int data_count, myComplex4 *data_in, myComplex4 *data_out, myComplex4 *coef) { __v2di *x_real_in = (__v2di*)&data_in[0].real; __v2di *x_imag_in = (__v2di*)&data_in[0].imag; __v2di *y_real_in = (__v2di*)&data_in[1].real; __v2di *y_imag_in = (__v2di*)&data_in[1].imag; __v2di *c_real_in = (__v2di*)&coef[0].real; __v2di *c_imag_in = (__v2di*)&coef[0].imag; __v2di *out_add_real = (__v2di*)&data_out[0].real; __v2di *out_add_imag = (__v2di*)&data_out[0].imag; __v2di *out_sub_real = (__v2di*)&data_out[data_count/2].real; __v2di *out_sub_imag = (__v2di*)&data_out[data_count/2].imag; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < data_count/2; ++i) { __v2di x_real = x_real_in[4*i]; __v2di x_imag = x_imag_in[4*i]; __v2di y_real = y_real_in[4*i]; __v2di y_imag = y_imag_in[4*i]; __v2di c_real = c_real_in[2*i]; __v2di c_imag = c_imag_in[2*i]; __v2di cy_real = __builtin_e2k_qpfsubs(__builtin_e2k_qpfmuls(c_real, y_real), __builtin_e2k_qpfmuls(c_imag, y_imag)); __v2di cy_imag = __builtin_e2k_qpfadds(__builtin_e2k_qpfmuls(c_real, y_imag), __builtin_e2k_qpfmuls(c_imag, y_real)); out_add_real[2*i] = __builtin_e2k_qpfadds(x_real, cy_real); out_add_imag[2*i] = __builtin_e2k_qpfadds(x_imag, cy_imag); out_sub_real[2*i] = __builtin_e2k_qpfsubs(x_real, cy_real); out_sub_imag[2*i] = __builtin_e2k_qpfsubs(x_imag, cy_imag); } }
Основной цикл на ассемблере
.L1875: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=4, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=32 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=2, asz=4, abs=16, disp=0 } .L1577: { loop_mode qpfmuls,0,sm %b[43], %b[52], %b[45] qpfsubs,1,sm %b[46], %b[24], %b[60] staaqp,2 %b[64], %aad1[ %aasti3 + _f32s,_lts0 0x10 ] qpfmul_adds,3,sm %b[12], %b[13], %b[51], %b[14] qpfadds,4,sm %b[46], %b[24], %b[63] staaqp,5 %b[67], %aad2[ %aasti4 + _f32s,_lts0 0x10 ] movaqp,0 area=1, ind=16, am=1, be=0, %b[37] movaqp,1 area=1, ind=0, am=0, be=0, %b[0] movaqp,3 area=0, ind=16, am=0, be=0, %b[1] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qpfmuls,0,sm %b[43], %b[7], %b[64] qpfsubs,1,sm %b[35], %b[61], %b[67] staaqp,2 %b[71], %aad1[ %aasti3 ] incr,2 %aaincr3 qpfmul_rsubs,3,sm %b[12], %b[58], %b[70], %b[51] qpfadds,4,sm %b[35], %b[61], %b[72] staaqp,5 %b[76], %aad2[ %aasti4 ] incr,5 %aaincr3 movaqp,0 area=0, ind=0, am=0, be=0, %b[13] movaqp,1 area=0, ind=16, am=1, be=0, %b[24] movaqp,3 area=0, ind=0, am=1, be=0, %b[46] }
Теоретическая скорость: 8 комплексных чисел за 2 такта (8/2) = 32 Байт/такт
Замеры скорости

Компилятор не сумел векторизовать вариант stage_radix2_vector4_etalon в 4 раза.
До этого он успешно векторизовал вариант stage_radix2_vector2_etalon в 2 раза.
Итоги по stage_radix2_vector4


stage_radix4_vector2
Один проход по stage_radix4_vector2 совершает ту же работу, что 2 прохода по stage_radix2. Поэтому скорость stage_radix4_vector2 будем умножать на 2 для удобства сравнения с stage_radix2 (этот факт подписан на оси графика и в выводе консоли).
1. stage_radix4_vector2_etalon
Эталонный вариант для сравнения на корректность.
Код на Си
void stage_radix4_vector2_etalon(int data_count, myComplex2 *data_in, myComplex2 *data_out, myComplex2 *coefC, myComplex2 *coefD, myComplex2 *coefE) { myComplex2 *x_in = &data_in[0]; myComplex2 *y_in = &data_in[1]; myComplex2 *z_in = &data_in[2]; myComplex2 *w_in = &data_in[3]; myComplex2 *c_in = coefC; myComplex2 *d_in = coefD; myComplex2 *e_in = coefE; myComplex2 *out_0 = &data_out[0*data_count/4]; myComplex2 *out_1 = &data_out[1*data_count/4]; myComplex2 *out_2 = &data_out[2*data_count/4]; myComplex2 *out_3 = &data_out[3*data_count/4]; #pragma ivdep #pragma unroll(1) // #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { myComplex2 x = x_in[4*i]; myComplex2 y = y_in[4*i]; myComplex2 z = z_in[4*i]; myComplex2 w = w_in[4*i]; myComplex2 c = c_in[i]; myComplex2 d = d_in[i]; myComplex2 e = e_in[i]; myComplex2 cy = complex2_mul(c, y); myComplex2 dz = complex2_mul(d, z); myComplex2 ew = complex2_mul(e, w); myComplex2 add02 = complex2_add( x, dz); myComplex2 sub02 = complex2_sub( x, dz); myComplex2 add13 = complex2_add(cy, ew); myComplex2 sub13 = complex2_sub(cy, ew); myComplex2 sub13i = complex2_mul_i(sub13); out_0[i] = complex2_add(add02, add13); out_1[i] = complex2_sub(sub02, sub13i); out_2[i] = complex2_sub(add02, add13); out_3[i] = complex2_add(sub02, sub13i); } }
Основной цикл на ассемблере
.L2695: { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=3, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=4, abs=0, disp=32 } { fapb ct=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=4, asz=3, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=3, asz=4, abs=16, disp=0 } { fapb ct=1, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=4, abs=16, disp=0 } .L1896: { loop_mode pfmul_adds,0,sm %b[48], %b[70], %b[42], %b[0] pfsub_rsubs,1,sm %b[37], %b[73], %b[59], %b[4] pfsub_adds,2,sm %b[37], %b[73], %b[59], %b[1] pfmuls,4,sm %b[63], %b[62], %b[40] pfsubs,5,sm %b[78], %b[74], %b[57] } { loop_mode pfmul_rsubs,0,sm %b[48], %b[64], %b[83], %b[70] pfadd_adds,1,sm %b[25], %b[82], %b[90], %b[77] pfadd_rsubs,2,sm %b[25], %b[82], %b[90], %b[73] pfmuls,4,sm %b[63], %b[68], %b[81] pfadds,5,sm %b[78], %b[74], %b[88] movad,0 area=2, ind=0, am=0, be=0, %b[42] movad,1 area=2, ind=8, am=1, be=0, %b[59] movad,2 area=1, ind=0, am=0, be=0, %b[37] movad,3 area=0, ind=16, am=0, be=0, %b[58] } { loop_mode pfmul_rsubs,0,sm %b[69], %b[13], %b[84], %b[74] pfmul_rsubs,1,sm %b[45], %b[34], %b[85], %b[78] staad,2 %b[49], %aad3[ %aasti7 ] pfmuls,3,sm %b[14], %b[53], %b[82] pfmuls,4,sm %b[54], %b[20], %b[83] staad,5 %b[26], %aad1[ %aasti5 ] movad,0 area=1, ind=0, am=0, be=0, %b[63] movad,1 area=0, ind=8, am=0, be=0, %b[25] movad,2 area=1, ind=8, am=1, be=0, %b[48] movad,3 area=0, ind=24, am=0, be=0, %b[64] } { loop_mode pfmul_adds,0,sm %b[67], %b[53], %b[86], %b[34] pfmul_adds,1,sm %b[45], %b[22], %b[87], %b[69] staad,2 %b[10], %aad4[ %aasti8 + _f32s,_lts0 0x8 ] pfmuls,3,sm %b[12], %b[9], %b[84] pfmuls,4,sm %b[54], %b[32], %b[85] staad,5 %b[7], %aad2[ %aasti6 + _f32s,_lts0 0x8 ] movad,0 area=0, ind=0, am=0, be=0, %b[13] movad,1 area=0, ind=24, am=0, be=0, %b[49] movad,2 area=0, ind=0, am=0, be=0, %b[26] movad,3 area=0, ind=8, am=1, be=0, %b[14] } { loop_mode pfsub_rsubs,0,sm %b[23], %b[80], %b[55], %b[45] pfsub_adds,1,sm %b[23], %b[80], %b[55], %b[22] staad,2 %b[6], %aad3[ %aasti7 + _f32s,_lts0 0x8 ] incr,2 %aaincr3 pfsubs,4,sm %b[2], %b[38], %b[53] staad,5 %b[3], %aad1[ %aasti5 + _f32s,_lts0 0x8 ] incr,5 %aaincr3 movad,0 area=1, ind=8, am=1, be=0, %b[10] movad,1 area=0, ind=16, am=1, be=0, %b[7] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END pfadd_adds,0,sm %b[35], %b[71], %b[56], %b[6] pfadd_rsubs,1,sm %b[35], %b[71], %b[56], %b[3] staad,2 %b[79], %aad4[ %aasti8 ] incr,2 %aaincr3 pfadds,4,sm %b[38], %b[2], %b[54] staad,5 %b[75], %aad2[ %aasti6 ] incr,5 %aaincr3 }
Теоретическая скорость: 8 комплексных чисел за 6 тактов (8/6) = 10.67 Байт/такт
Двойная теоретическая скорость: 21.33 Байт/такт
Замеры скорости

2. stage_radix4_vector2
Прямая векторизация базового алгоритма в 2 раза с помощью инструкций SIMD64.
Код на Си
void stage_radix4_vector2(int data_count, myComplex2 *data_in, myComplex2 *data_out, myComplex2 *coefC, myComplex2 *coefD, myComplex2 *coefE) { uint64_t *x_real_in = (uint64_t*)&data_in[0].real; uint64_t *x_imag_in = (uint64_t*)&data_in[0].imag; uint64_t *y_real_in = (uint64_t*)&data_in[1].real; uint64_t *y_imag_in = (uint64_t*)&data_in[1].imag; uint64_t *z_real_in = (uint64_t*)&data_in[2].real; uint64_t *z_imag_in = (uint64_t*)&data_in[2].imag; uint64_t *w_real_in = (uint64_t*)&data_in[3].real; uint64_t *w_imag_in = (uint64_t*)&data_in[3].imag; uint64_t *c_real_in = (uint64_t*)&coefC[0].real; uint64_t *c_imag_in = (uint64_t*)&coefC[0].imag; uint64_t *d_real_in = (uint64_t*)&coefD[0].real; uint64_t *d_imag_in = (uint64_t*)&coefD[0].imag; uint64_t *e_real_in = (uint64_t*)&coefE[0].real; uint64_t *e_imag_in = (uint64_t*)&coefE[0].imag; uint64_t *out_0_real = (uint64_t*)&data_out[0*data_count/4].real; uint64_t *out_0_imag = (uint64_t*)&data_out[0*data_count/4].imag; uint64_t *out_1_real = (uint64_t*)&data_out[1*data_count/4].real; uint64_t *out_1_imag = (uint64_t*)&data_out[1*data_count/4].imag; uint64_t *out_2_real = (uint64_t*)&data_out[2*data_count/4].real; uint64_t *out_2_imag = (uint64_t*)&data_out[2*data_count/4].imag; uint64_t *out_3_real = (uint64_t*)&data_out[3*data_count/4].real; uint64_t *out_3_imag = (uint64_t*)&data_out[3*data_count/4].imag; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { uint64_t x_real = x_real_in[8*i]; uint64_t x_imag = x_imag_in[8*i]; uint64_t y_real = y_real_in[8*i]; uint64_t y_imag = y_imag_in[8*i]; uint64_t z_real = z_real_in[8*i]; uint64_t z_imag = z_imag_in[8*i]; uint64_t w_real = w_real_in[8*i]; uint64_t w_imag = w_imag_in[8*i]; uint64_t c_real = c_real_in[2*i]; uint64_t c_imag = c_imag_in[2*i]; uint64_t d_real = d_real_in[2*i]; uint64_t d_imag = d_imag_in[2*i]; uint64_t e_real = e_real_in[2*i]; uint64_t e_imag = e_imag_in[2*i]; uint64_t cy_real = __builtin_e2k_pfsubs(__builtin_e2k_pfmuls(c_real, y_real), __builtin_e2k_pfmuls(c_imag, y_imag)); uint64_t cy_imag = __builtin_e2k_pfadds(__builtin_e2k_pfmuls(c_real, y_imag), __builtin_e2k_pfmuls(c_imag, y_real)); uint64_t dz_real = __builtin_e2k_pfsubs(__builtin_e2k_pfmuls(d_real, z_real), __builtin_e2k_pfmuls(d_imag, z_imag)); uint64_t dz_imag = __builtin_e2k_pfadds(__builtin_e2k_pfmuls(d_real, z_imag), __builtin_e2k_pfmuls(d_imag, z_real)); uint64_t ew_real = __builtin_e2k_pfsubs(__builtin_e2k_pfmuls(e_real, w_real), __builtin_e2k_pfmuls(e_imag, w_imag)); uint64_t ew_imag = __builtin_e2k_pfadds(__builtin_e2k_pfmuls(e_real, w_imag), __builtin_e2k_pfmuls(e_imag, w_real)); uint64_t add02_real = __builtin_e2k_pfadds( x_real, dz_real); uint64_t add02_imag = __builtin_e2k_pfadds( x_imag, dz_imag); uint64_t sub02_real = __builtin_e2k_pfsubs( x_real, dz_real); uint64_t sub02_imag = __builtin_e2k_pfsubs( x_imag, dz_imag); uint64_t add13_real = __builtin_e2k_pfadds(cy_real, ew_real); uint64_t add13_imag = __builtin_e2k_pfadds(cy_imag, ew_imag); uint64_t sub13_real = __builtin_e2k_pfsubs(cy_real, ew_real); // uint64_t sub13_imag = __builtin_e2k_pfsubs(cy_imag, ew_imag); // uint64_t sub13i_real = -sub13_imag; uint64_t sub13i_real = __builtin_e2k_pfsubs(ew_imag, cy_imag); uint64_t sub13i_imag = sub13_real; out_0_real[2*i] = __builtin_e2k_pfadds(add02_real, add13_real); out_0_imag[2*i] = __builtin_e2k_pfadds(add02_imag, add13_imag); out_1_real[2*i] = __builtin_e2k_pfsubs(sub02_real, sub13i_real); out_1_imag[2*i] = __builtin_e2k_pfsubs(sub02_imag, sub13i_imag); out_2_real[2*i] = __builtin_e2k_pfsubs(add02_real, add13_real); out_2_imag[2*i] = __builtin_e2k_pfsubs(add02_imag, add13_imag); out_3_real[2*i] = __builtin_e2k_pfadds(sub02_real, sub13i_real); out_3_imag[2*i] = __builtin_e2k_pfadds(sub02_imag, sub13i_imag); } }
Основной цикл на ассемблере
.L3659: { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=3, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=4, abs=0, disp=32 } { fapb ct=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=4, asz=3, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=3, asz=4, abs=16, disp=0 } { fapb ct=1, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=4, abs=16, disp=0 } .L3151: { loop_mode pfmul_adds,0,sm %b[48], %b[70], %b[42], %b[0] pfsub_rsubs,1,sm %b[37], %b[73], %b[59], %b[4] pfsub_adds,2,sm %b[37], %b[73], %b[59], %b[1] pfmuls,4,sm %b[63], %b[62], %b[40] pfsubs,5,sm %b[78], %b[74], %b[57] } { loop_mode pfmul_rsubs,0,sm %b[48], %b[64], %b[83], %b[70] pfadd_adds,1,sm %b[25], %b[82], %b[90], %b[77] pfadd_rsubs,2,sm %b[25], %b[82], %b[90], %b[73] pfmuls,4,sm %b[63], %b[68], %b[81] pfadds,5,sm %b[78], %b[74], %b[88] movad,0 area=2, ind=0, am=0, be=0, %b[42] movad,1 area=2, ind=8, am=1, be=0, %b[59] movad,2 area=1, ind=0, am=0, be=0, %b[37] movad,3 area=0, ind=16, am=0, be=0, %b[58] } { loop_mode pfmul_rsubs,0,sm %b[69], %b[13], %b[84], %b[74] pfmul_rsubs,1,sm %b[45], %b[34], %b[85], %b[78] staad,2 %b[49], %aad3[ %aasti7 ] pfmuls,3,sm %b[14], %b[53], %b[82] pfmuls,4,sm %b[54], %b[20], %b[83] staad,5 %b[26], %aad1[ %aasti5 ] movad,0 area=1, ind=0, am=0, be=0, %b[63] movad,1 area=0, ind=8, am=0, be=0, %b[25] movad,2 area=1, ind=8, am=1, be=0, %b[48] movad,3 area=0, ind=24, am=0, be=0, %b[64] } { loop_mode pfmul_adds,0,sm %b[67], %b[53], %b[86], %b[34] pfmul_adds,1,sm %b[45], %b[22], %b[87], %b[69] staad,2 %b[10], %aad4[ %aasti8 + _f32s,_lts0 0x8 ] pfmuls,3,sm %b[12], %b[9], %b[84] pfmuls,4,sm %b[54], %b[32], %b[85] staad,5 %b[7], %aad2[ %aasti6 + _f32s,_lts0 0x8 ] movad,0 area=0, ind=0, am=0, be=0, %b[13] movad,1 area=0, ind=24, am=0, be=0, %b[49] movad,2 area=0, ind=0, am=0, be=0, %b[26] movad,3 area=0, ind=8, am=1, be=0, %b[14] } { loop_mode pfsub_rsubs,0,sm %b[23], %b[80], %b[55], %b[45] pfsub_adds,1,sm %b[23], %b[80], %b[55], %b[22] staad,2 %b[6], %aad3[ %aasti7 + _f32s,_lts0 0x8 ] incr,2 %aaincr3 pfsubs,4,sm %b[2], %b[38], %b[53] staad,5 %b[3], %aad1[ %aasti5 + _f32s,_lts0 0x8 ] incr,5 %aaincr3 movad,0 area=1, ind=8, am=1, be=0, %b[10] movad,1 area=0, ind=16, am=1, be=0, %b[7] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END pfadd_adds,0,sm %b[35], %b[71], %b[56], %b[6] pfadd_rsubs,1,sm %b[35], %b[71], %b[56], %b[3] staad,2 %b[79], %aad4[ %aasti8 ] incr,2 %aaincr3 pfadds,4,sm %b[38], %b[2], %b[54] staad,5 %b[75], %aad2[ %aasti6 ] incr,5 %aaincr3 }
Код на ассемблере получился такой же, как в эталонном варианте, то есть компилятор самостоятельно векторизовал эталонный вариант в 2 раза.
Теоретическая скорость: 8 комплексных чисел за 6 тактов (8/6) = 10.67 Байт/такт
Двойная теоретическая скорость: 21.33 Байт/такт
Замеры скорости

3. stage_radix4_vector2_unroll2
Здесь происходит раскрутка цикла в 2 раза с помощью директивы unroll.
Код на Си
void stage_radix4_vector2_unroll2(int data_count, myComplex2 *data_in, myComplex2 *data_out, myComplex2 *coefC, myComplex2 *coefD, myComplex2 *coefE) { uint64_t *x_real_in = (uint64_t*)&data_in[0].real; uint64_t *x_imag_in = (uint64_t*)&data_in[0].imag; uint64_t *y_real_in = (uint64_t*)&data_in[1].real; uint64_t *y_imag_in = (uint64_t*)&data_in[1].imag; uint64_t *z_real_in = (uint64_t*)&data_in[2].real; uint64_t *z_imag_in = (uint64_t*)&data_in[2].imag; uint64_t *w_real_in = (uint64_t*)&data_in[3].real; uint64_t *w_imag_in = (uint64_t*)&data_in[3].imag; uint64_t *c_real_in = (uint64_t*)&coefC[0].real; uint64_t *c_imag_in = (uint64_t*)&coefC[0].imag; uint64_t *d_real_in = (uint64_t*)&coefD[0].real; uint64_t *d_imag_in = (uint64_t*)&coefD[0].imag; uint64_t *e_real_in = (uint64_t*)&coefE[0].real; uint64_t *e_imag_in = (uint64_t*)&coefE[0].imag; uint64_t *out_0_real = (uint64_t*)&data_out[0*data_count/4].real; uint64_t *out_0_imag = (uint64_t*)&data_out[0*data_count/4].imag; uint64_t *out_1_real = (uint64_t*)&data_out[1*data_count/4].real; uint64_t *out_1_imag = (uint64_t*)&data_out[1*data_count/4].imag; uint64_t *out_2_real = (uint64_t*)&data_out[2*data_count/4].real; uint64_t *out_2_imag = (uint64_t*)&data_out[2*data_count/4].imag; uint64_t *out_3_real = (uint64_t*)&data_out[3*data_count/4].real; uint64_t *out_3_imag = (uint64_t*)&data_out[3*data_count/4].imag; #pragma ivdep #pragma unroll(2) #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { uint64_t x_real = x_real_in[8*i]; uint64_t x_imag = x_imag_in[8*i]; uint64_t y_real = y_real_in[8*i]; uint64_t y_imag = y_imag_in[8*i]; uint64_t z_real = z_real_in[8*i]; uint64_t z_imag = z_imag_in[8*i]; uint64_t w_real = w_real_in[8*i]; uint64_t w_imag = w_imag_in[8*i]; uint64_t c_real = c_real_in[2*i]; uint64_t c_imag = c_imag_in[2*i]; uint64_t d_real = d_real_in[2*i]; uint64_t d_imag = d_imag_in[2*i]; uint64_t e_real = e_real_in[2*i]; uint64_t e_imag = e_imag_in[2*i]; uint64_t cy_real = __builtin_e2k_pfsubs(__builtin_e2k_pfmuls(c_real, y_real), __builtin_e2k_pfmuls(c_imag, y_imag)); uint64_t cy_imag = __builtin_e2k_pfadds(__builtin_e2k_pfmuls(c_real, y_imag), __builtin_e2k_pfmuls(c_imag, y_real)); uint64_t dz_real = __builtin_e2k_pfsubs(__builtin_e2k_pfmuls(d_real, z_real), __builtin_e2k_pfmuls(d_imag, z_imag)); uint64_t dz_imag = __builtin_e2k_pfadds(__builtin_e2k_pfmuls(d_real, z_imag), __builtin_e2k_pfmuls(d_imag, z_real)); uint64_t ew_real = __builtin_e2k_pfsubs(__builtin_e2k_pfmuls(e_real, w_real), __builtin_e2k_pfmuls(e_imag, w_imag)); uint64_t ew_imag = __builtin_e2k_pfadds(__builtin_e2k_pfmuls(e_real, w_imag), __builtin_e2k_pfmuls(e_imag, w_real)); uint64_t add02_real = __builtin_e2k_pfadds( x_real, dz_real); uint64_t add02_imag = __builtin_e2k_pfadds( x_imag, dz_imag); uint64_t sub02_real = __builtin_e2k_pfsubs( x_real, dz_real); uint64_t sub02_imag = __builtin_e2k_pfsubs( x_imag, dz_imag); uint64_t add13_real = __builtin_e2k_pfadds(cy_real, ew_real); uint64_t add13_imag = __builtin_e2k_pfadds(cy_imag, ew_imag); uint64_t sub13_real = __builtin_e2k_pfsubs(cy_real, ew_real); // uint64_t sub13_imag = __builtin_e2k_pfsubs(cy_imag, ew_imag); // uint64_t sub13i_real = -sub13_imag; uint64_t sub13i_real = __builtin_e2k_pfsubs(ew_imag, cy_imag); uint64_t sub13i_imag = sub13_real; out_0_real[2*i] = __builtin_e2k_pfadds(add02_real, add13_real); out_0_imag[2*i] = __builtin_e2k_pfadds(add02_imag, add13_imag); out_1_real[2*i] = __builtin_e2k_pfsubs(sub02_real, sub13i_real); out_1_imag[2*i] = __builtin_e2k_pfsubs(sub02_imag, sub13i_imag); out_2_real[2*i] = __builtin_e2k_pfsubs(add02_real, add13_real); out_2_imag[2*i] = __builtin_e2k_pfsubs(add02_imag, add13_imag); out_3_real[2*i] = __builtin_e2k_pfadds(sub02_real, sub13i_real); out_3_imag[2*i] = __builtin_e2k_pfadds(sub02_imag, sub13i_imag); } }
Основной цикл на ассемблере
.L4871: { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=3, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=3, abs=0, disp=16 } { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=3, abs=8, disp=64 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=3, abs=8, disp=32 } { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=2, ind=4, asz=3, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=3, abs=16, disp=96 } { fapb ct=1, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=3, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=2, ind=3, asz=3, abs=24, disp=0 } .L4115: { loop_mode pfadd_adds,0,sm %b[56], %b[48], %b[97], %b[91] pfadd_rsubs,1,sm %b[56], %b[48], %b[97], %b[88] pfsubs,2,sm %b[83], %b[73], %b[98] pfmul_adds,3,sm %b[35], %b[49], %b[91], %b[0] pfmuls,4,sm %b[88], %b[4], %b[1] pfmuls,5,sm %b[44], %b[72], %b[95] } { loop_mode pfmul_rsubs,0,sm %b[35], %b[29], %g18, %b[11] pfmul_adds,1,sm %b[11], %b[63], %b[96], %b[72] pfadds,2,sm %b[73], %b[83], %b[83] pfmul_adds,3,sm %b[30], %b[72], %b[94], %b[29] pfmuls,4,sm %b[36], %b[47], %g18 pfadds,5,sm %g16, %g17, %b[73] } { loop_mode pfmul_rsubs,0,sm %b[68], %b[14], %b[24], %g17 pfmul_rsubs,1,sm %b[45], %b[34], %b[3], %g16 pfsubs,2,sm %g16, %g17, %b[96] pfmul_adds,3,sm %b[18], %b[60], %b[92], %b[3] pfmuls,4,sm %b[10], %b[61], %b[94] pfsubs,5,sm %b[5], %b[31], %b[92] } { loop_mode pfmul_adds,0,sm %b[45], %b[6], %b[71], %b[71] pfmul_adds,1,sm %b[68], %b[41], %b[85], %b[81] staad,2 %b[86], %aad3[ %aasti7 + _f32s,_lts0 0x10 ] pfmul_rsubs,3,sm %b[18], %b[67], %b[93], %b[24] pfadds,4,sm %b[31], %b[5], %b[85] staad,5 %b[81], %aad1[ %aasti5 + _f32s,_lts0 0x10 ] movad,0 area=3, ind=0, am=0, be=0, %b[14] movad,1 area=3, ind=8, am=1, be=0, %b[18] movad,2 area=3, ind=0, am=0, be=0, %b[5] movad,3 area=3, ind=8, am=0, be=0, %b[6] } { loop_mode pfsub_rsubs,0,sm %b[19], %b[13], %b[98], %b[84] pfsub_adds,1,sm %b[19], %b[13], %b[98], %b[79] staad,2 %b[84], %aad4[ %aasti8 + _f32s,_lts0 0x18 ] pfmul_rsubs,3,sm %b[30], %b[64], %b[95], %b[35] pfsubs,4,sm %b[37], %b[26], %b[93] staad,5 %b[79], %aad2[ %aasti6 + _f32s,_lts0 0x18 ] movad,0 area=2, ind=24, am=0, be=0, %b[86] movad,1 area=1, ind=16, am=0, be=0, %b[30] movad,2 area=3, ind=16, am=0, be=0, %b[31] movad,3 area=3, ind=24, am=1, be=0, %b[34] } { loop_mode pfadd_adds,0,sm %b[25], %b[2], %b[83], %b[82] pfadd_rsubs,1,sm %b[25], %b[2], %b[83], %b[77] staad,2 %b[82], %aad3[ %aasti7 + _f32s,_lts0 0x18 ] pfmuls,3,sm %g19, %b[12], %b[83] pfadds,4,sm %b[37], %b[26], %b[95] staad,5 %b[77], %aad1[ %aasti5 + _f32s,_lts0 0x18 ] movad,0 area=2, ind=16, am=0, be=0, %b[41] movad,1 area=2, ind=0, am=0, be=0, %b[26] movad,2 area=2, ind=24, am=0, be=0, %b[37] movad,3 area=0, ind=8, am=0, be=0, %g19 } { loop_mode pfsub_rsubs,0,sm %b[25], %b[2], %b[96], %b[80] pfsub_adds,1,sm %b[25], %b[2], %b[96], %b[75] staad,2 %b[80], %aad4[ %aasti8 + _f32s,_lts0 0x10 ] pfmul_rsubs,3,sm %b[9], %b[40], %b[94], %b[44] pfmuls,4,sm %b[10], %b[40], %b[94] staad,5 %b[75], %aad2[ %aasti6 + _f32s,_lts0 0x10 ] movad,0 area=2, ind=8, am=1, be=0, %b[40] movad,1 area=1, ind=24, am=0, be=0, %b[2] movad,2 area=2, ind=0, am=0, be=0, %b[25] movad,3 area=2, ind=16, am=0, be=0, %b[10] } { loop_mode pfadd_adds,0,sm %b[19], %b[13], %b[73], %b[78] pfadd_rsubs,1,sm %b[19], %b[13], %b[73], %b[73] staad,2 %b[78], %aad3[ %aasti7 ] pfmuls,4,sm %b[36], %b[27], %b[89] staad,5 %b[89], %aad1[ %aasti5 ] movad,0 area=1, ind=8, am=1, be=0, %b[19] movad,1 area=1, ind=0, am=0, be=0, %b[13] movad,2 area=2, ind=8, am=1, be=0, %b[45] movad,3 area=1, ind=0, am=0, be=0, %b[36] } { loop_mode pfsub_rsubs,0,sm %b[54], %b[46], %b[92], %b[76] pfsub_adds,1,sm %b[54], %b[46], %b[92], %b[87] staad,2 %b[76], %aad4[ %aasti8 + _f32s,_lts0 0x8 ] pfmuls,4,sm %b[42], %b[62], %b[92] staad,5 %b[87], %aad2[ %aasti6 + _f32s,_lts0 0x8 ] movad,0 area=0, ind=8, am=0, be=0, %b[49] movad,1 area=0, ind=0, am=0, be=0, %b[48] movad,2 area=1, ind=8, am=0, be=0, %b[57] movad,3 area=1, ind=24, am=0, be=0, %b[56] } { loop_mode pfadd_adds,0,sm %b[55], %b[72], %b[85], %b[74] pfadd_rsubs,1,sm %b[55], %b[72], %b[85], %b[85] staad,2 %b[90], %aad3[ %aasti7 + _f32s,_lts0 0x8 ] incr,2 %aaincr3 pfmuls,3,sm %b[86], %b[30], %b[67] pfmuls,4,sm %b[20], %b[65], %b[90] staad,5 %b[74], %aad1[ %aasti5 + _f32s,_lts0 0x8 ] incr,5 %aaincr3 movad,0 area=0, ind=24, am=1, be=0, %b[68] movad,1 area=0, ind=16, am=0, be=0, %b[60] movad,2 area=1, ind=16, am=1, be=0, %b[63] movad,3 area=0, ind=0, am=1, be=0, %b[64] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END pfsub_rsubs,0,sm %b[55], %b[72], %b[93], %b[88] pfsub_adds,1,sm %b[55], %b[72], %b[93], %b[72] staad,2 %b[91], %aad4[ %aasti8 ] incr,2 %aaincr3 pfmuls,3,sm %g19, %b[37], %b[20] pfmuls,4,sm %b[20], %b[58], %b[91] staad,5 %b[88], %aad2[ %aasti6 ] incr,5 %aaincr3 }
Теоретическая скорость: 16 комплексных чисел за 11 тактов (16/11) = 11.64 Байт/такт
Двойная теоретическая скорость: 23.27 Байт/такт
Замеры скорости

4. stage_radix4_vector2_unroll3
Здесь происходит раскрутка цикла в 3 раза с помощью директивы unroll.
Код на Си
void stage_radix4_vector2_unroll3(int data_count, myComplex2 *data_in, myComplex2 *data_out, myComplex2 *coefC, myComplex2 *coefD, myComplex2 *coefE) { uint64_t *x_real_in = (uint64_t*)&data_in[0].real; uint64_t *x_imag_in = (uint64_t*)&data_in[0].imag; uint64_t *y_real_in = (uint64_t*)&data_in[1].real; uint64_t *y_imag_in = (uint64_t*)&data_in[1].imag; uint64_t *z_real_in = (uint64_t*)&data_in[2].real; uint64_t *z_imag_in = (uint64_t*)&data_in[2].imag; uint64_t *w_real_in = (uint64_t*)&data_in[3].real; uint64_t *w_imag_in = (uint64_t*)&data_in[3].imag; uint64_t *c_real_in = (uint64_t*)&coefC[0].real; uint64_t *c_imag_in = (uint64_t*)&coefC[0].imag; uint64_t *d_real_in = (uint64_t*)&coefD[0].real; uint64_t *d_imag_in = (uint64_t*)&coefD[0].imag; uint64_t *e_real_in = (uint64_t*)&coefE[0].real; uint64_t *e_imag_in = (uint64_t*)&coefE[0].imag; uint64_t *out_0_real = (uint64_t*)&data_out[0*data_count/4].real; uint64_t *out_0_imag = (uint64_t*)&data_out[0*data_count/4].imag; uint64_t *out_1_real = (uint64_t*)&data_out[1*data_count/4].real; uint64_t *out_1_imag = (uint64_t*)&data_out[1*data_count/4].imag; uint64_t *out_2_real = (uint64_t*)&data_out[2*data_count/4].real; uint64_t *out_2_imag = (uint64_t*)&data_out[2*data_count/4].imag; uint64_t *out_3_real = (uint64_t*)&data_out[3*data_count/4].real; uint64_t *out_3_imag = (uint64_t*)&data_out[3*data_count/4].imag; #pragma ivdep #pragma unroll(3) #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { uint64_t x_real = x_real_in[8*i]; uint64_t x_imag = x_imag_in[8*i]; uint64_t y_real = y_real_in[8*i]; uint64_t y_imag = y_imag_in[8*i]; uint64_t z_real = z_real_in[8*i]; uint64_t z_imag = z_imag_in[8*i]; uint64_t w_real = w_real_in[8*i]; uint64_t w_imag = w_imag_in[8*i]; uint64_t c_real = c_real_in[2*i]; uint64_t c_imag = c_imag_in[2*i]; uint64_t d_real = d_real_in[2*i]; uint64_t d_imag = d_imag_in[2*i]; uint64_t e_real = e_real_in[2*i]; uint64_t e_imag = e_imag_in[2*i]; uint64_t cy_real = __builtin_e2k_pfsubs(__builtin_e2k_pfmuls(c_real, y_real), __builtin_e2k_pfmuls(c_imag, y_imag)); uint64_t cy_imag = __builtin_e2k_pfadds(__builtin_e2k_pfmuls(c_real, y_imag), __builtin_e2k_pfmuls(c_imag, y_real)); uint64_t dz_real = __builtin_e2k_pfsubs(__builtin_e2k_pfmuls(d_real, z_real), __builtin_e2k_pfmuls(d_imag, z_imag)); uint64_t dz_imag = __builtin_e2k_pfadds(__builtin_e2k_pfmuls(d_real, z_imag), __builtin_e2k_pfmuls(d_imag, z_real)); uint64_t ew_real = __builtin_e2k_pfsubs(__builtin_e2k_pfmuls(e_real, w_real), __builtin_e2k_pfmuls(e_imag, w_imag)); uint64_t ew_imag = __builtin_e2k_pfadds(__builtin_e2k_pfmuls(e_real, w_imag), __builtin_e2k_pfmuls(e_imag, w_real)); uint64_t add02_real = __builtin_e2k_pfadds( x_real, dz_real); uint64_t add02_imag = __builtin_e2k_pfadds( x_imag, dz_imag); uint64_t sub02_real = __builtin_e2k_pfsubs( x_real, dz_real); uint64_t sub02_imag = __builtin_e2k_pfsubs( x_imag, dz_imag); uint64_t add13_real = __builtin_e2k_pfadds(cy_real, ew_real); uint64_t add13_imag = __builtin_e2k_pfadds(cy_imag, ew_imag); uint64_t sub13_real = __builtin_e2k_pfsubs(cy_real, ew_real); // uint64_t sub13_imag = __builtin_e2k_pfsubs(cy_imag, ew_imag); // uint64_t sub13i_real = -sub13_imag; uint64_t sub13i_real = __builtin_e2k_pfsubs(ew_imag, cy_imag); uint64_t sub13i_imag = sub13_real; out_0_real[2*i] = __builtin_e2k_pfadds(add02_real, add13_real); out_0_imag[2*i] = __builtin_e2k_pfadds(add02_imag, add13_imag); out_1_real[2*i] = __builtin_e2k_pfsubs(sub02_real, sub13i_real); out_1_imag[2*i] = __builtin_e2k_pfsubs(sub02_imag, sub13i_imag); out_2_real[2*i] = __builtin_e2k_pfsubs(add02_real, add13_real); out_2_imag[2*i] = __builtin_e2k_pfsubs(add02_imag, add13_imag); out_3_real[2*i] = __builtin_e2k_pfadds(sub02_real, sub13i_real); out_3_imag[2*i] = __builtin_e2k_pfadds(sub02_imag, sub13i_imag); } }
Основной цикл на ассемблере
.L6432: { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=2, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=2, abs=0, disp=32 } { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=2, abs=4, disp=64 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=2, abs=4, disp=96 } { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=2, abs=8, disp=128 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=2, abs=8, disp=160 } { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=2, ind=4, asz=2, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=2, ind=3, asz=2, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=2, ind=2, asz=3, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=4, asz=3, abs=16, disp=32 } { fapb ct=1, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=3, asz=3, abs=24, disp=32 fapb dpl=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=3, abs=24, disp=32 } .L4979: { loop_mode pfsub_rsubs,0,sm %b[40], %g19, %g20, %g21 pfadd_adds,1,sm %b[77], %g16, %g17, %g18 pfsubs,2,sm %g22, %g23, %g24 pfmuls,3,sm %b[6], %b[32], %g27 pfmuls,4,sm %b[56], %b[65], %g26 pfmuls,5,sm %b[72], %b[48], %g25 } { loop_mode pfadd_adds,0,sm %b[68], %g28, %g29, %g31 pfadd_rsubs,1,sm %b[68], %g28, %g29, %g30 pfadds,2,sm %g23, %g22, %r0 pfmuls,3,sm %b[55], %b[61], %r3 pfmuls,4,sm %b[56], %b[102], %r2 pfmuls,5,sm %b[52], %b[99], %r1 } { loop_mode pfsub_rsubs,0,sm %b[68], %g28, %r4, %r7 pfsub_adds,1,sm %b[68], %g28, %r4, %r5 pfmuls,2,sm %b[20], %b[78], %r8 pfmuls,3,sm %b[18], %b[90], %r11 pfmuls,4,sm %b[51], %b[95], %r10 pfmuls,5,sm %b[41], %b[83], %r9 } { loop_mode pfadd_adds,0,sm %b[40], %g19, %r12, %r14 pfadd_rsubs,1,sm %b[40], %g19, %r12, %r13 pfmuls,2,sm %b[35], %b[112], %r18 pfmuls,3,sm %b[55], %b[116], %r20 pfmuls,4,sm %b[35], %b[113], %r19 pfadds,5,sm %r15, %r16, %r17 } { loop_mode pfsub_rsubs,0,sm %b[77], %g16, %g24, %g24 pfsub_adds,1,sm %b[77], %g16, %g24, %r22 staad,2 %b[119], %aad3[ %aasti7 + _f32s,_lts0 0x10 ] pfmuls,3,sm %b[70], %b[104], %b[0] pfsubs,4,sm %r23, %r24, %r25 staad,5 %r21, %aad1[ %aasti5 + _f32s,_lts0 0x10 ] } { loop_mode pfadd_adds,0,sm %b[14], %b[103], %r0, %b[118] pfadd_rsubs,1,sm %b[14], %b[103], %r0, %r27 staad,2 %b[118], %aad4[ %aasti8 + _f32s,_lts0 0x18 ] pfmul_adds,3,sm %b[23], %b[107], %r1, %b[20] pfadds,4,sm %r24, %r23, %r0 staad,5 %r26, %aad2[ %aasti6 + _f32s,_lts0 0x18 ] movad,0 area=5, ind=0, am=0, be=0, %b[1] movad,1 area=5, ind=8, am=1, be=0, %b[14] } { loop_mode pfmul_adds,0,sm %b[69], %b[106], %g25, %r24 pfmul_rsubs,1,sm %b[7], %b[92], %r8, %g16 staad,2 %r29, %aad3[ %aasti7 + _f32s,_lts0 0x18 ] pfmul_adds,3,sm %b[45], %b[65], %r2, %r23 pfsubs,4,sm %r15, %r16, %r1 staad,5 %r28, %aad1[ %aasti5 + _f32s,_lts0 0x18 ] movad,0 area=4, ind=0, am=0, be=0, %b[40] movad,1 area=4, ind=16, am=0, be=0, %b[41] movad,2 area=5, ind=0, am=0, be=0, %b[7] movad,3 area=5, ind=8, am=1, be=0, %b[35] } { loop_mode pfmul_rsubs,0,sm %b[45], %b[102], %g26, %r16 pfmul_rsubs,1,sm %b[19], %b[113], %r18, %g19 staad,2 %r31, %aad3[ %aasti7 + _f32s,_lts0 0x28 ] pfmul_rsubs,3,sm %b[23], %b[99], %r34, %b[55] pfsubs,4,sm %r32, %r33, %g25 staad,5 %r30, %aad1[ %aasti5 + _f32s,_lts0 0x28 ] movad,0 area=4, ind=8, am=1, be=0, %b[51] movad,1 area=4, ind=24, am=0, be=0, %b[52] movad,2 area=4, ind=0, am=0, be=0, %b[23] movad,3 area=4, ind=8, am=1, be=0, %b[45] } { loop_mode pfmul_adds,0,sm %b[29], %b[89], %r10, %g23 pfmul_adds,1,sm %b[13], %b[86], %r9, %g22 staad,2 %r36, %aad2[ %aasti6 + _f32s,_lts1 0x10 ] pfmul_rsubs,3,sm %b[11], %b[81], %r37, %r33 pfadds,4,sm %r32, %r33, %g17 staad,5 %r35, %aad2[ %aasti6 + _f32s,_lts0 0x20 ] movad,0 area=3, ind=0, am=0, be=0, %b[56] movad,1 area=3, ind=16, am=0, be=0, %b[65] movad,2 area=3, ind=0, am=0, be=0, %b[13] movad,3 area=3, ind=8, am=0, be=0, %b[29] } { loop_mode pfmul_rsubs,0,sm %b[69], %b[48], %b[2], %r15 pfmul_adds,1,sm %b[19], %b[112], %r19, %g28 staad,2 %r39, %aad1[ %aasti5 ] pfmul_rsubs,3,sm %b[27], %b[93], %r41, %r32 pfsubs,4,sm %r40, %b[80], %g20 staad,5 %r38, %aad4[ %aasti8 + _f32s,_lts0 0x10 ] movad,0 area=3, ind=8, am=1, be=0, %b[2] movad,1 area=3, ind=24, am=0, be=0, %b[68] movad,2 area=3, ind=16, am=0, be=0, %b[19] movad,3 area=3, ind=24, am=1, be=0, %b[48] } { loop_mode pfsub_rsubs,0,sm %b[28], %b[57], %r25, %b[117] pfsub_adds,1,sm %b[28], %b[57], %r25, %r21 staad,2 %g21, %aad3[ %aasti7 ] pfmul_adds,3,sm %b[60], %b[117], %g27, %b[78] pfmuls,4,sm %b[6], %b[117], %g18 staad,5 %g18, %aad4[ %aasti8 + _f32s,_lts0 0x20 ] movad,0 area=2, ind=0, am=0, be=0, %b[69] movad,1 area=2, ind=8, am=0, be=0, %b[6] movad,2 area=2, ind=8, am=0, be=0, %b[72] movad,3 area=2, ind=16, am=0, be=0, %b[77] } { loop_mode pfadd_adds,0,sm %b[98], %b[22], %r0, %b[116] pfadd_rsubs,1,sm %b[98], %b[22], %r0, %r26 staad,2 %g31, %aad4[ %aasti8 + _f32s,_lts0 0x8 ] pfmul_adds,3,sm %b[44], %b[116], %r3, %r40 pfadds,4,sm %b[80], %r40, %g29 staad,5 %g30, %aad2[ %aasti6 + _f32s,_lts0 0x8 ] movad,0 area=2, ind=16, am=0, be=0, %b[89] movad,1 area=2, ind=24, am=1, be=0, %b[83] movad,2 area=2, ind=0, am=1, be=0, %b[86] movad,3 area=2, ind=24, am=0, be=0, %b[80] } { loop_mode pfsub_rsubs,0,sm %b[98], %b[22], %r1, %r29 pfsub_adds,1,sm %b[98], %b[22], %r1, %r28 staad,2 %r7, %aad3[ %aasti7 + _f32s,_lts0 0x8 ] pfmul_adds,3,sm %b[5], %b[76], %r11, %b[99] pfsubs,4,sm %r42, %r43, %r4 staad,5 %r5, %aad1[ %aasti5 + _f32s,_lts0 0x8 ] movad,0 area=1, ind=8, am=0, be=0, %b[92] movad,1 area=1, ind=0, am=0, be=0, %b[22] movad,2 area=1, ind=16, am=0, be=0, %b[98] movad,3 area=1, ind=0, am=0, be=0, %b[95] } { loop_mode pfsub_rsubs,0,sm %b[12], %b[101], %g25, %r31 pfsub_adds,1,sm %b[12], %b[101], %g25, %r30 staad,2 %r14, %aad4[ %aasti8 ] pfmul_rsubs,3,sm %b[44], %b[61], %r20, %r43 pfadds,4,sm %r42, %r43, %r12 staad,5 %r13, %aad2[ %aasti6 ] movad,0 area=1, ind=16, am=0, be=0, %b[44] movad,1 area=1, ind=24, am=1, be=0, %b[102] movad,2 area=1, ind=24, am=0, be=0, %b[61] movad,3 area=1, ind=8, am=1, be=0, %b[103] } { loop_mode pfadd_rsubs,0,sm %b[28], %b[57], %r17, %r36 pfadd_rsubs,1,sm %b[75], %g16, %g17, %r35 staad,2 %g24, %aad3[ %aasti7 + _f32s,_lts0 0x20 ] incr,2 %aaincr3 pfmul_rsubs,3,sm %b[60], %b[32], %g18, %r42 pfmuls,4,sm %b[37], %b[82], %r37 staad,5 %r22, %aad1[ %aasti5 + _f32s,_lts0 0x20 ] incr,5 %aaincr3 movad,0 area=0, ind=8, am=0, be=0, %b[60] movad,1 area=0, ind=0, am=0, be=0, %b[32] movad,2 area=0, ind=0, am=0, be=0, %b[107] movad,3 area=0, ind=8, am=0, be=0, %b[106] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END pfsub_adds,0,sm %b[38], %g19, %g20, %r39 pfadd_adds,1,sm %b[28], %b[57], %r17, %r38 staad,2 %b[118], %aad4[ %aasti8 + _f32s,_lts0 0x28 ] incr,2 %aaincr3 pfmuls,3,sm %b[47], %b[85], %r41 pfmuls,4,sm %b[50], %b[105], %r34 staad,5 %r27, %aad2[ %aasti6 + _f32s,_lts0 0x28 ] incr,5 %aaincr3 movad,0 area=0, ind=24, am=1, be=0, %b[113] movad,1 area=0, ind=16, am=0, be=0, %b[28] movad,2 area=0, ind=24, am=1, be=0, %b[112] movad,3 area=0, ind=16, am=0, be=0, %b[57] }
Теоретическая скорость: 24 комплексных числа за 16 тактов (24/16) = 12 Байт/такт
Двойная теоретическая скорость: 24 Байт/такт
Замеры скорости

Итоги по stage_radix4_vector2


stage_radix4_vector4
Один проход по stage_radix4_vector4 совершает ту же работу, что 2 прохода по stage_radix2. Поэтому скорость stage_radix4_vector4 будем умножать на 2 для удобства сравнения с stage_radix2 (этот факт подписан на оси графика и в выводе консоли).
1. stage_radix4_vector4_etalon
Эталонный вариант для сравнения на корректность.
Код на Си
void stage_radix4_vector4_etalon(int data_count, myComplex4 *data_in, myComplex4 *data_out, myComplex4 *coefC, myComplex4 *coefD, myComplex4 *coefE) { myComplex4 *x_in = &data_in[0]; myComplex4 *y_in = &data_in[1]; myComplex4 *z_in = &data_in[2]; myComplex4 *w_in = &data_in[3]; myComplex4 *c_in = coefC; myComplex4 *d_in = coefD; myComplex4 *e_in = coefE; myComplex4 *out_0 = &data_out[0*data_count/4]; myComplex4 *out_1 = &data_out[1*data_count/4]; myComplex4 *out_2 = &data_out[2*data_count/4]; myComplex4 *out_3 = &data_out[3*data_count/4]; #pragma ivdep #pragma unroll(1) // #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { myComplex4 x = x_in[4*i]; myComplex4 y = y_in[4*i]; myComplex4 z = z_in[4*i]; myComplex4 w = w_in[4*i]; myComplex4 c = c_in[i]; myComplex4 d = d_in[i]; myComplex4 e = e_in[i]; myComplex4 cy = complex4_mul(c, y); myComplex4 dz = complex4_mul(d, z); myComplex4 ew = complex4_mul(e, w); myComplex4 add02 = complex4_add( x, dz); myComplex4 sub02 = complex4_sub( x, dz); myComplex4 add13 = complex4_add(cy, ew); myComplex4 sub13 = complex4_sub(cy, ew); myComplex4 sub13i = complex4_mul_i(sub13); out_0[i] = complex4_add(add02, add13); out_1[i] = complex4_sub(sub02, sub13i); out_2[i] = complex4_sub(add02, add13); out_3[i] = complex4_add(sub02, sub13i); } }
Основной цикл на ассемблере
.L4263: { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=3, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=3, abs=0, disp=16 } { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=3, abs=8, disp=64 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=3, abs=8, disp=32 } { fapb ct=0, dcd=0, fmt=4, mrng=0, d=0, incr=2, ind=4, asz=3, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=3, abs=16, disp=96 } { fapb ct=1, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=3, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=0, d=0, incr=2, ind=3, asz=3, abs=24, disp=0 } .L3418: { loop_mode pfsub_rsubs,0,sm %b[68], %b[38], %b[93], %b[98] pfsub_adds,1,sm %b[68], %b[38], %b[93], %b[93] pfsubs,2,sm %b[87], %b[77], %b[102] pfmul_adds,3,sm %b[19], %b[59], %b[98], %b[0] pfmuls,4,sm %b[47], %b[53], %b[1] pfmuls,5,sm %b[26], %b[11], %b[100] } { loop_mode pfmul_adds,0,sm %b[22], %b[32], %b[91], %b[26] pfmul_adds,1,sm %b[18], %b[55], %b[97], %b[32] pfadds,2,sm %b[87], %b[77], %b[91] pfmul_adds,3,sm %b[14], %b[45], %b[99], %b[38] pfmuls,4,sm %b[26], %b[15], %b[87] pfadds,5,sm %g16, %g17, %b[77] } { loop_mode pfmul_rsubs,0,sm %b[22], %b[72], %b[6], %g17 pfmul_rsubs,1,sm %b[18], %b[76], %b[3], %g16 pfsubs,2,sm %g16, %g17, %b[99] pfmul_adds,3,sm %b[10], %b[23], %b[101], %b[23] pfadds,4,sm %b[34], %b[28], %b[76] pfsubs,5,sm %b[25], %b[40], %b[97] movad,0 area=3, ind=8, am=1, be=0, %b[6] movad,1 area=3, ind=0, am=0, be=0, %b[18] movad,2 area=3, ind=0, am=0, be=0, %b[3] movad,3 area=3, ind=16, am=0, be=0, %b[22] } { loop_mode pfmul_rsubs,0,sm %b[10], %b[71], %b[56], %b[75] pfmul_rsubs,1,sm %b[14], %b[75], %b[31], %b[85] staad,2 %b[90], %aad3[ %aasti7 + _f32s,_lts0 0x18 ] pfmul_rsubs,3,sm %b[19], %b[51], %b[95], %b[31] pfadds,4,sm %b[40], %b[25], %b[90] staad,5 %b[85], %aad1[ %aasti5 + _f32s,_lts0 0x18 ] movad,0 area=2, ind=0, am=0, be=0, %b[14] movad,1 area=2, ind=8, am=0, be=0, %b[10] movad,2 area=2, ind=24, am=0, be=0, %b[19] movad,3 area=0, ind=8, am=0, be=0, %b[25] } { loop_mode pfsub_rsubs,0,sm %b[39], %b[2], %b[102], %b[88] pfsub_adds,1,sm %b[39], %b[2], %b[102], %b[83] staad,2 %b[89], %aad4[ %aasti8 + _f32s,_lts0 0x8 ] pfmul_adds,3,sm %b[7], %b[15], %b[100], %b[40] pfmuls,4,sm %b[83], %b[70], %b[89] staad,5 %b[88], %aad2[ %aasti6 + _f32s,_lts0 0x8 ] movad,0 area=2, ind=16, am=0, be=0, %b[45] movad,1 area=2, ind=24, am=1, be=0, %b[44] movad,2 area=3, ind=8, am=0, be=0, %b[15] movad,3 area=1, ind=24, am=0, be=0, %b[41] } { loop_mode pfadd_adds,0,sm %b[52], %b[33], %b[91], %b[87] pfadd_rsubs,1,sm %b[52], %b[33], %b[91], %b[86] staad,2 %b[86], %aad3[ %aasti7 + _f32s,_lts0 0x10 ] pfmul_rsubs,3,sm %b[7], %b[11], %b[87], %b[34] pfsubs,4,sm %b[28], %b[34], %b[91] staad,5 %b[81], %aad1[ %aasti5 + _f32s,_lts0 0x10 ] movad,0 area=1, ind=16, am=0, be=0, %b[11] movad,1 area=1, ind=0, am=0, be=0, %b[7] movad,2 area=2, ind=16, am=0, be=0, %b[28] movad,3 area=0, ind=0, am=1, be=0, %b[81] } { loop_mode pfsub_rsubs,0,sm %b[65], %b[42], %b[99], %b[84] pfsub_adds,1,sm %b[65], %b[42], %b[99], %b[79] staad,2 %b[84], %aad4[ %aasti8 ] pfmuls,4,sm %b[47], %b[74], %b[95] staad,5 %b[79], %aad2[ %aasti6 ] movad,0 area=1, ind=24, am=0, be=0, %b[55] movad,1 area=1, ind=8, am=1, be=0, %b[47] movad,2 area=3, ind=24, am=1, be=0, %b[56] movad,3 area=1, ind=16, am=0, be=0, %b[51] } { loop_mode pfadd_adds,0,sm %b[66], %b[36], %b[77], %b[82] pfadd_rsubs,1,sm %b[66], %b[36], %b[77], %b[77] staad,2 %b[96], %aad3[ %aasti7 + _f32s,_lts0 0x8 ] pfmuls,4,sm %b[58], %b[49], %b[96] staad,5 %b[82], %aad1[ %aasti5 + _f32s,_lts0 0x8 ] movad,0 area=0, ind=0, am=0, be=0, %b[60] movad,1 area=0, ind=16, am=0, be=0, %b[59] movad,2 area=2, ind=8, am=1, be=0, %b[67] movad,3 area=2, ind=0, am=0, be=0, %b[68] } { loop_mode pfsub_rsubs,0,sm %b[52], %b[33], %b[97], %b[94] pfsub_adds,1,sm %b[52], %b[33], %b[97], %b[80] staad,2 %b[94], %aad2[ %aasti6 + _f32s,_lts1 0x10 ] pfmuls,3,sm %b[25], %b[19], %b[52] pfmuls,4,sm %b[46], %b[73], %b[97] staad,5 %b[80], %aad2[ %aasti6 + _f32s,_lts0 0x18 ] incr,5 %aaincr3 movad,0 area=0, ind=8, am=1, be=0, %b[46] movad,1 area=0, ind=24, am=0, be=0, %b[33] movad,2 area=1, ind=8, am=1, be=0, %b[71] movad,3 area=1, ind=0, am=0, be=0, %b[72] } { loop_mode pfadd_rsubs,0,sm %b[65], %b[42], %b[76], %b[92] pfadd_rsubs,1,sm %b[39], %b[2], %b[90], %b[78] staad,2 %b[92], %aad4[ %aasti8 + _f32s,_lts1 0x10 ] pfmuls,3,sm %b[44], %b[41], %b[27] pfmuls,4,sm %b[27], %b[69], %b[99] staad,5 %b[78], %aad4[ %aasti8 + _f32s,_lts0 0x18 ] incr,5 %aaincr3 } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END pfadd_adds,0,sm %b[65], %b[42], %b[76], %b[90] pfadd_adds,1,sm %b[39], %b[2], %b[90], %b[76] staad,2 %b[98], %aad3[ %aasti7 ] incr,2 %aaincr3 pfmuls,3,sm %b[81], %b[28], %b[2] pfmuls,4,sm %b[58], %b[57], %b[93] staad,5 %b[93], %aad1[ %aasti5 ] incr,5 %aaincr3 }
Теоретическая скорость: 16 комплексных чисел за 11 тактов (16/11) = 11.64 Байт/такт
Двойная теоретическая скорость: 23.27 Байт/такт
Замеры скорости

2. stage_radix4_vector4
Прямая векторизация базового алгоритма в 4 раза с помощью инструкций SIMD128.
Код на Си
void stage_radix4_vector4(int data_count, myComplex4 *data_in, myComplex4 *data_out, myComplex4 *coefC, myComplex4 *coefD, myComplex4 *coefE) { __v2di *x_real_in = (__v2di*)&data_in[0].real; __v2di *x_imag_in = (__v2di*)&data_in[0].imag; __v2di *y_real_in = (__v2di*)&data_in[1].real; __v2di *y_imag_in = (__v2di*)&data_in[1].imag; __v2di *z_real_in = (__v2di*)&data_in[2].real; __v2di *z_imag_in = (__v2di*)&data_in[2].imag; __v2di *w_real_in = (__v2di*)&data_in[3].real; __v2di *w_imag_in = (__v2di*)&data_in[3].imag; __v2di *c_real_in = (__v2di*)&coefC[0].real; __v2di *c_imag_in = (__v2di*)&coefC[0].imag; __v2di *d_real_in = (__v2di*)&coefD[0].real; __v2di *d_imag_in = (__v2di*)&coefD[0].imag; __v2di *e_real_in = (__v2di*)&coefE[0].real; __v2di *e_imag_in = (__v2di*)&coefE[0].imag; __v2di *out_0_real = (__v2di*)&data_out[0*data_count/4].real; __v2di *out_0_imag = (__v2di*)&data_out[0*data_count/4].imag; __v2di *out_1_real = (__v2di*)&data_out[1*data_count/4].real; __v2di *out_1_imag = (__v2di*)&data_out[1*data_count/4].imag; __v2di *out_2_real = (__v2di*)&data_out[2*data_count/4].real; __v2di *out_2_imag = (__v2di*)&data_out[2*data_count/4].imag; __v2di *out_3_real = (__v2di*)&data_out[3*data_count/4].real; __v2di *out_3_imag = (__v2di*)&data_out[3*data_count/4].imag; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { __v2di x_real = x_real_in[8*i]; __v2di x_imag = x_imag_in[8*i]; __v2di y_real = y_real_in[8*i]; __v2di y_imag = y_imag_in[8*i]; __v2di z_real = z_real_in[8*i]; __v2di z_imag = z_imag_in[8*i]; __v2di w_real = w_real_in[8*i]; __v2di w_imag = w_imag_in[8*i]; __v2di c_real = c_real_in[2*i]; __v2di c_imag = c_imag_in[2*i]; __v2di d_real = d_real_in[2*i]; __v2di d_imag = d_imag_in[2*i]; __v2di e_real = e_real_in[2*i]; __v2di e_imag = e_imag_in[2*i]; __v2di cy_real = __builtin_e2k_qpfsubs(__builtin_e2k_qpfmuls(c_real, y_real), __builtin_e2k_qpfmuls(c_imag, y_imag)); __v2di cy_imag = __builtin_e2k_qpfadds(__builtin_e2k_qpfmuls(c_real, y_imag), __builtin_e2k_qpfmuls(c_imag, y_real)); __v2di dz_real = __builtin_e2k_qpfsubs(__builtin_e2k_qpfmuls(d_real, z_real), __builtin_e2k_qpfmuls(d_imag, z_imag)); __v2di dz_imag = __builtin_e2k_qpfadds(__builtin_e2k_qpfmuls(d_real, z_imag), __builtin_e2k_qpfmuls(d_imag, z_real)); __v2di ew_real = __builtin_e2k_qpfsubs(__builtin_e2k_qpfmuls(e_real, w_real), __builtin_e2k_qpfmuls(e_imag, w_imag)); __v2di ew_imag = __builtin_e2k_qpfadds(__builtin_e2k_qpfmuls(e_real, w_imag), __builtin_e2k_qpfmuls(e_imag, w_real)); __v2di add02_real = __builtin_e2k_qpfadds( x_real, dz_real); __v2di add02_imag = __builtin_e2k_qpfadds( x_imag, dz_imag); __v2di sub02_real = __builtin_e2k_qpfsubs( x_real, dz_real); __v2di sub02_imag = __builtin_e2k_qpfsubs( x_imag, dz_imag); __v2di add13_real = __builtin_e2k_qpfadds(cy_real, ew_real); __v2di add13_imag = __builtin_e2k_qpfadds(cy_imag, ew_imag); __v2di sub13_real = __builtin_e2k_qpfsubs(cy_real, ew_real); // __v2di sub13_imag = __builtin_e2k_qpfsubs(cy_imag, ew_imag); // __v2di sub13i_real = -sub13_imag; __v2di sub13i_real = __builtin_e2k_qpfsubs(ew_imag, cy_imag); __v2di sub13i_imag = sub13_real; out_0_real[2*i] = __builtin_e2k_qpfadds(add02_real, add13_real); out_0_imag[2*i] = __builtin_e2k_qpfadds(add02_imag, add13_imag); out_1_real[2*i] = __builtin_e2k_qpfsubs(sub02_real, sub13i_real); out_1_imag[2*i] = __builtin_e2k_qpfsubs(sub02_imag, sub13i_imag); out_2_real[2*i] = __builtin_e2k_qpfsubs(add02_real, add13_real); out_2_imag[2*i] = __builtin_e2k_qpfsubs(add02_imag, add13_imag); out_3_real[2*i] = __builtin_e2k_qpfadds(sub02_real, sub13i_real); out_3_imag[2*i] = __builtin_e2k_qpfadds(sub02_imag, sub13i_imag); } }
Основной цикл на ассемблере
.L5045: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=3, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=3, abs=0, disp=32 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=3, abs=8, disp=64 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=3, abs=8, disp=96 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=4, asz=3, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=3, asz=4, abs=16, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=2, asz=3, abs=24, disp=0 } .L4371: { loop_mode qpfmul_adds,0,sm %b[6], %b[69], %b[57], %b[1] qpfsub_rsubs,1,sm %b[30], %b[74], %b[58], %b[47] qpfsub_adds,2,sm %b[30], %b[74], %b[58], %b[5] qpfmuls,4,sm %b[62], %b[61], %b[55] qpfsubs,5,sm %b[77], %b[73], %b[56] movaqp,1 area=3, ind=0, am=0, be=0, %b[0] } { loop_mode qpfmul_rsubs,0,sm %b[6], %b[63], %b[84], %b[69] qpfadd_adds,1,sm %b[29], %b[81], %b[89], %b[78] qpfadd_rsubs,2,sm %b[29], %b[81], %b[89], %b[74] qpfmuls,4,sm %b[62], %b[67], %b[82] qpfadds,5,sm %b[77], %b[73], %b[87] movaqp,0 area=3, ind=16, am=1, be=0, %b[58] movaqp,1 area=2, ind=0, am=0, be=0, %b[30] movaqp,3 area=1, ind=0, am=0, be=0, %b[57] } { loop_mode qpfmul_rsubs,0,sm %b[36], %b[17], %b[83], %b[73] qpfmul_rsubs,1,sm %b[14], %b[70], %b[86], %b[77] staaqp,2 %b[46], %aad3[ %aasti7 ] qpfmuls,3,sm %b[18], %b[50], %b[81] qpfmuls,4,sm %b[42], %b[35], %b[84] staaqp,5 %b[41], %aad1[ %aasti5 ] movaqp,0 area=1, ind=0, am=0, be=0, %b[62] movaqp,1 area=1, ind=16, am=1, be=0, %b[29] movaqp,2 area=2, ind=0, am=0, be=0, %b[6] movaqp,3 area=1, ind=16, am=1, be=0, %b[63] } { loop_mode qpfmul_adds,0,sm %b[34], %b[50], %b[85], %b[41] qpfmul_adds,1,sm %b[14], %b[37], %b[88], %b[70] staaqp,2 %b[53], %aad4[ %aasti8 + _f32s,_lts0 0x10 ] qpfmuls,3,sm %b[16], %b[13], %b[83] qpfmuls,4,sm %b[42], %b[68], %b[86] staaqp,5 %b[11], %aad2[ %aasti6 + _f32s,_lts0 0x10 ] movaqp,0 area=0, ind=16, am=1, be=0, %b[18] movaqp,1 area=0, ind=0, am=0, be=0, %b[17] movaqp,2 area=2, ind=16, am=1, be=0, %b[36] movaqp,3 area=0, ind=16, am=0, be=0, %b[46] } { loop_mode qpfsub_rsubs,0,sm %b[27], %b[79], %b[52], %b[42] qpfsub_adds,1,sm %b[27], %b[79], %b[52], %b[37] staaqp,2 %b[49], %aad3[ %aasti7 + _f32s,_lts0 0x10 ] incr,2 %aaincr3 qpfsubs,4,sm %b[3], %b[45], %b[50] staaqp,5 %b[7], %aad1[ %aasti5 + _f32s,_lts0 0x10 ] incr,5 %aaincr3 movaqp,1 area=2, ind=16, am=1, be=0, %b[14] movaqp,3 area=0, ind=0, am=1, be=0, %b[11] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qpfadd_adds,0,sm %b[28], %b[72], %b[54], %b[49] qpfadd_rsubs,1,sm %b[28], %b[72], %b[54], %b[7] staaqp,2 %b[80], %aad4[ %aasti8 ] incr,2 %aaincr3 qpfadds,4,sm %b[45], %b[3], %b[52] staaqp,5 %b[76], %aad2[ %aasti6 ] incr,5 %aaincr3 }
Теоретическая скорость: 16 комплексных чисел за 6 тактов (16/6) = 21.33 Байт/такт
Двойная теоретическая скорость: 42.67 Байт/такт
Замеры скорости

3. stage_radix4_vector4_unroll2
Здесь происходит раскрутка цикла в 2 раза с помощью директивы unroll.
Код на Си
void stage_radix4_vector4_unroll2(int data_count, myComplex4 *data_in, myComplex4 *data_out, myComplex4 *coefC, myComplex4 *coefD, myComplex4 *coefE) { __v2di *x_real_in = (__v2di*)&data_in[0].real; __v2di *x_imag_in = (__v2di*)&data_in[0].imag; __v2di *y_real_in = (__v2di*)&data_in[1].real; __v2di *y_imag_in = (__v2di*)&data_in[1].imag; __v2di *z_real_in = (__v2di*)&data_in[2].real; __v2di *z_imag_in = (__v2di*)&data_in[2].imag; __v2di *w_real_in = (__v2di*)&data_in[3].real; __v2di *w_imag_in = (__v2di*)&data_in[3].imag; __v2di *c_real_in = (__v2di*)&coefC[0].real; __v2di *c_imag_in = (__v2di*)&coefC[0].imag; __v2di *d_real_in = (__v2di*)&coefD[0].real; __v2di *d_imag_in = (__v2di*)&coefD[0].imag; __v2di *e_real_in = (__v2di*)&coefE[0].real; __v2di *e_imag_in = (__v2di*)&coefE[0].imag; __v2di *out_0_real = (__v2di*)&data_out[0*data_count/4].real; __v2di *out_0_imag = (__v2di*)&data_out[0*data_count/4].imag; __v2di *out_1_real = (__v2di*)&data_out[1*data_count/4].real; __v2di *out_1_imag = (__v2di*)&data_out[1*data_count/4].imag; __v2di *out_2_real = (__v2di*)&data_out[2*data_count/4].real; __v2di *out_2_imag = (__v2di*)&data_out[2*data_count/4].imag; __v2di *out_3_real = (__v2di*)&data_out[3*data_count/4].real; __v2di *out_3_imag = (__v2di*)&data_out[3*data_count/4].imag; #pragma ivdep #pragma unroll(2) #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { __v2di x_real = x_real_in[8*i]; __v2di x_imag = x_imag_in[8*i]; __v2di y_real = y_real_in[8*i]; __v2di y_imag = y_imag_in[8*i]; __v2di z_real = z_real_in[8*i]; __v2di z_imag = z_imag_in[8*i]; __v2di w_real = w_real_in[8*i]; __v2di w_imag = w_imag_in[8*i]; __v2di c_real = c_real_in[2*i]; __v2di c_imag = c_imag_in[2*i]; __v2di d_real = d_real_in[2*i]; __v2di d_imag = d_imag_in[2*i]; __v2di e_real = e_real_in[2*i]; __v2di e_imag = e_imag_in[2*i]; __v2di cy_real = __builtin_e2k_qpfsubs(__builtin_e2k_qpfmuls(c_real, y_real), __builtin_e2k_qpfmuls(c_imag, y_imag)); __v2di cy_imag = __builtin_e2k_qpfadds(__builtin_e2k_qpfmuls(c_real, y_imag), __builtin_e2k_qpfmuls(c_imag, y_real)); __v2di dz_real = __builtin_e2k_qpfsubs(__builtin_e2k_qpfmuls(d_real, z_real), __builtin_e2k_qpfmuls(d_imag, z_imag)); __v2di dz_imag = __builtin_e2k_qpfadds(__builtin_e2k_qpfmuls(d_real, z_imag), __builtin_e2k_qpfmuls(d_imag, z_real)); __v2di ew_real = __builtin_e2k_qpfsubs(__builtin_e2k_qpfmuls(e_real, w_real), __builtin_e2k_qpfmuls(e_imag, w_imag)); __v2di ew_imag = __builtin_e2k_qpfadds(__builtin_e2k_qpfmuls(e_real, w_imag), __builtin_e2k_qpfmuls(e_imag, w_real)); __v2di add02_real = __builtin_e2k_qpfadds( x_real, dz_real); __v2di add02_imag = __builtin_e2k_qpfadds( x_imag, dz_imag); __v2di sub02_real = __builtin_e2k_qpfsubs( x_real, dz_real); __v2di sub02_imag = __builtin_e2k_qpfsubs( x_imag, dz_imag); __v2di add13_real = __builtin_e2k_qpfadds(cy_real, ew_real); __v2di add13_imag = __builtin_e2k_qpfadds(cy_imag, ew_imag); __v2di sub13_real = __builtin_e2k_qpfsubs(cy_real, ew_real); // __v2di sub13_imag = __builtin_e2k_qpfsubs(cy_imag, ew_imag); // __v2di sub13i_real = -sub13_imag; __v2di sub13i_real = __builtin_e2k_qpfsubs(ew_imag, cy_imag); __v2di sub13i_imag = sub13_real; out_0_real[2*i] = __builtin_e2k_qpfadds(add02_real, add13_real); out_0_imag[2*i] = __builtin_e2k_qpfadds(add02_imag, add13_imag); out_1_real[2*i] = __builtin_e2k_qpfsubs(sub02_real, sub13i_real); out_1_imag[2*i] = __builtin_e2k_qpfsubs(sub02_imag, sub13i_imag); out_2_real[2*i] = __builtin_e2k_qpfsubs(add02_real, add13_real); out_2_imag[2*i] = __builtin_e2k_qpfsubs(add02_imag, add13_imag); out_3_real[2*i] = __builtin_e2k_qpfadds(sub02_real, sub13i_real); out_3_imag[2*i] = __builtin_e2k_qpfadds(sub02_imag, sub13i_imag); } }
Основной цикл на ассемблере
.L6226: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=0, disp=32 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=4, disp=64 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=4, disp=96 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=8, disp=128 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=8, disp=160 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=12, disp=192 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=12, disp=224 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=4, asz=2, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=4, asz=2, abs=16, disp=32 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=3, asz=2, abs=20, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=3, asz=2, abs=20, disp=32 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=2, asz=3, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=2, asz=3, abs=24, disp=32 } .L5153: { loop_mode qpfadd_adds,0,sm %b[32], %b[21], %b[94], %b[65] qpfadd_rsubs,1,sm %b[32], %b[21], %b[94], %b[88] qpfsubs,2,sm %b[61], %b[65], %b[92] qpfmuls,3,sm %b[15], %b[40], %b[0] qpfmuls,4,sm %b[91], %b[48], %b[1] qpfmuls,5,sm %b[17], %b[9], %b[90] } { loop_mode qpfmul_rsubs,0,sm %b[22], %b[60], %g16, %b[21] qpfmul_adds,1,sm %b[8], %b[64], %b[95], %b[22] qpfsubs,2,sm %b[81], %b[70], %b[94] qpfmul_adds,3,sm %b[22], %b[57], %b[83], %b[17] qpfmuls,4,sm %b[35], %b[55], %g16 qpfsubs,5,sm %b[16], %b[10], %b[83] } { loop_mode qpfmul_rsubs,0,sm %b[14], %b[51], %b[3], %b[8] qpfmul_rsubs,1,sm %b[46], %b[52], %b[96], %b[14] qpfadds,2,sm %b[81], %b[70], %b[84] qpfmul_rsubs,3,sm %b[8], %b[38], %b[84], %b[3] qpfmuls,4,sm %b[53], %b[50], %b[70] qpfmuls,5,sm %b[91], %b[49], %b[81] } { loop_mode qpfmul_rsubs,0,sm %b[13], %b[9], %b[2], %b[68] qpfmul_rsubs,1,sm %b[56], %b[33], %b[93], %b[79] staaqp,2 %b[82], %aad3[ %aasti7 ] qpfmul_adds,3,sm %b[56], %b[68], %b[89], %b[2] qpfadds,4,sm %b[16], %b[10], %b[82] staaqp,5 %b[79], %aad1[ %aasti5 ] } { loop_mode qpfsub_rsubs,0,sm %b[29], %b[5], %b[92], %b[80] qpfsub_adds,1,sm %b[29], %b[5], %b[92], %b[77] staaqp,2 %b[80], %aad2[ %aasti6 + _f32s,_lts1 0x10 ] qpfmul_adds,3,sm %b[13], %b[42], %b[90], %b[16] qpfsubs,4,sm %b[18], %b[4], %b[90] staaqp,5 %b[77], %aad1[ %aasti5 + _f32s,_lts0 0x30 ] movaqp,0 area=6, ind=0, am=0, be=0, %b[10] movaqp,1 area=6, ind=16, am=1, be=0, %b[89] movaqp,2 area=6, ind=0, am=0, be=0, %b[9] movaqp,3 area=6, ind=16, am=1, be=0, %b[13] } { loop_mode qpfadd_rsubs,0,sm %b[47], %b[24], %b[87], %b[78] qpfsub_adds,1,sm %b[30], %b[19], %b[94], %b[75] staaqp,2 %b[75], %aad3[ %aasti7 + _f32s,_lts1 0x30 ] qpfmuls,3,sm %g17, %b[66], %b[91] qpfadds,4,sm %b[4], %b[18], %b[92] staaqp,5 %b[78], %aad2[ %aasti6 + _f32s,_lts0 0x20 ] movaqp,0 area=5, ind=0, am=0, be=0, %b[4] movaqp,1 area=5, ind=16, am=1, be=0, %b[32] movaqp,2 area=5, ind=0, am=0, be=0, %b[18] movaqp,3 area=5, ind=16, am=1, be=0, %b[33] } { loop_mode qpfsub_rsubs,0,sm %b[30], %b[19], %b[94], %b[73] qpfadd_rsubs,1,sm %b[41], %b[23], %b[84], %b[76] staaqp,2 %b[76], %aad4[ %aasti8 + _f32s,_lts0 0x10 ] qpfmuls,4,sm %b[34], %b[36], %b[93] staaqp,5 %b[73], %aad1[ %aasti5 + _f32s,_lts0 0x10 ] movaqp,0 area=4, ind=0, am=0, be=0, %b[42] movaqp,1 area=4, ind=16, am=1, be=0, %b[51] movaqp,2 area=3, ind=16, am=0, be=0, %b[38] movaqp,3 area=1, ind=16, am=0, be=0, %b[46] } { loop_mode qpfadd_adds,0,sm %b[47], %b[24], %b[87], %b[74] qpfsub_adds,1,sm %b[47], %b[24], %b[83], %b[71] staaqp,2 %b[74], %aad3[ %aasti7 + _f32s,_lts1 0x10 ] qpfmuls,3,sm %g17, %b[31], %b[87] qpfmuls,4,sm %b[53], %b[67], %b[94] staaqp,5 %b[71], %aad4[ %aasti8 + _f32s,_lts0 0x20 ] movaqp,0 area=3, ind=0, am=0, be=0, %b[56] movaqp,1 area=3, ind=16, am=1, be=0, %b[53] movaqp,2 area=4, ind=0, am=0, be=0, %b[52] movaqp,3 area=4, ind=16, am=1, be=0, %g17 } { loop_mode qpfsub_rsubs,0,sm %b[47], %b[24], %b[83], %b[72] qpfadd_adds,1,sm %b[41], %b[23], %b[84], %b[69] staaqp,2 %b[72], %aad4[ %aasti8 ] qpfmul_adds,3,sm %b[12], %b[48], %b[81], %b[57] qpfmuls,4,sm %b[35], %b[58], %b[81] staaqp,5 %b[69], %aad2[ %aasti6 ] movaqp,0 area=2, ind=0, am=0, be=0, %b[35] movaqp,1 area=2, ind=16, am=1, be=0, %b[24] movaqp,2 area=1, ind=0, am=1, be=0, %b[47] movaqp,3 area=0, ind=0, am=0, be=0, %b[48] } { loop_mode qpfadd_adds,0,sm %b[29], %b[5], %b[82], %b[70] qpfadd_rsubs,1,sm %b[29], %b[5], %b[82], %b[67] staaqp,2 %b[85], %aad3[ %aasti7 + _f32s,_lts0 0x20 ] incr,2 %aaincr3 qpfmul_adds,3,sm %b[44], %b[67], %b[70], %b[61] qpfmuls,4,sm %b[34], %b[62], %b[82] staaqp,5 %b[86], %aad1[ %aasti5 + _f32s,_lts0 0x20 ] incr,5 %aaincr3 movaqp,0 area=1, ind=16, am=1, be=0, %b[60] movaqp,1 area=1, ind=0, am=0, be=0, %b[34] movaqp,2 area=3, ind=0, am=1, be=0, %b[5] movaqp,3 area=2, ind=0, am=0, be=0, %b[29] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qpfsub_rsubs,0,sm %b[41], %b[23], %b[90], %b[83] qpfsub_adds,1,sm %b[41], %b[23], %b[90], %b[84] staaqp,2 %b[65], %aad4[ %aasti8 + _f32s,_lts0 0x30 ] incr,2 %aaincr3 qpfadds,4,sm %b[63], %b[59], %b[85] staaqp,5 %b[88], %aad2[ %aasti6 + _f32s,_lts0 0x30 ] incr,5 %aaincr3 movaqp,0 area=0, ind=16, am=1, be=0, %b[41] movaqp,1 area=0, ind=0, am=0, be=0, %b[23] movaqp,2 area=2, ind=16, am=1, be=0, %b[64] movaqp,3 area=0, ind=16, am=1, be=0, %b[65] }
Теоретическая скорость: 32 комплексных числа за 11 тактов (32/11) = 23.27 Байт/такт
Двойная теоретическая скорость: 46.55 Байт/такт
Замеры скорости

4. stage_radix4_vector4_unroll3
Здесь происходит раскрутка цикла в 3 раза с помощью директивы unroll.
Код на Си
void stage_radix4_vector4_unroll3(int data_count, myComplex4 *data_in, myComplex4 *data_out, myComplex4 *coefC, myComplex4 *coefD, myComplex4 *coefE) { __v2di *x_real_in = (__v2di*)&data_in[0].real; __v2di *x_imag_in = (__v2di*)&data_in[0].imag; __v2di *y_real_in = (__v2di*)&data_in[1].real; __v2di *y_imag_in = (__v2di*)&data_in[1].imag; __v2di *z_real_in = (__v2di*)&data_in[2].real; __v2di *z_imag_in = (__v2di*)&data_in[2].imag; __v2di *w_real_in = (__v2di*)&data_in[3].real; __v2di *w_imag_in = (__v2di*)&data_in[3].imag; __v2di *c_real_in = (__v2di*)&coefC[0].real; __v2di *c_imag_in = (__v2di*)&coefC[0].imag; __v2di *d_real_in = (__v2di*)&coefD[0].real; __v2di *d_imag_in = (__v2di*)&coefD[0].imag; __v2di *e_real_in = (__v2di*)&coefE[0].real; __v2di *e_imag_in = (__v2di*)&coefE[0].imag; __v2di *out_0_real = (__v2di*)&data_out[0*data_count/4].real; __v2di *out_0_imag = (__v2di*)&data_out[0*data_count/4].imag; __v2di *out_1_real = (__v2di*)&data_out[1*data_count/4].real; __v2di *out_1_imag = (__v2di*)&data_out[1*data_count/4].imag; __v2di *out_2_real = (__v2di*)&data_out[2*data_count/4].real; __v2di *out_2_imag = (__v2di*)&data_out[2*data_count/4].imag; __v2di *out_3_real = (__v2di*)&data_out[3*data_count/4].real; __v2di *out_3_imag = (__v2di*)&data_out[3*data_count/4].imag; #pragma ivdep #pragma unroll(3) #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { __v2di x_real = x_real_in[8*i]; __v2di x_imag = x_imag_in[8*i]; __v2di y_real = y_real_in[8*i]; __v2di y_imag = y_imag_in[8*i]; __v2di z_real = z_real_in[8*i]; __v2di z_imag = z_imag_in[8*i]; __v2di w_real = w_real_in[8*i]; __v2di w_imag = w_imag_in[8*i]; __v2di c_real = c_real_in[2*i]; __v2di c_imag = c_imag_in[2*i]; __v2di d_real = d_real_in[2*i]; __v2di d_imag = d_imag_in[2*i]; __v2di e_real = e_real_in[2*i]; __v2di e_imag = e_imag_in[2*i]; __v2di cy_real = __builtin_e2k_qpfsubs(__builtin_e2k_qpfmuls(c_real, y_real), __builtin_e2k_qpfmuls(c_imag, y_imag)); __v2di cy_imag = __builtin_e2k_qpfadds(__builtin_e2k_qpfmuls(c_real, y_imag), __builtin_e2k_qpfmuls(c_imag, y_real)); __v2di dz_real = __builtin_e2k_qpfsubs(__builtin_e2k_qpfmuls(d_real, z_real), __builtin_e2k_qpfmuls(d_imag, z_imag)); __v2di dz_imag = __builtin_e2k_qpfadds(__builtin_e2k_qpfmuls(d_real, z_imag), __builtin_e2k_qpfmuls(d_imag, z_real)); __v2di ew_real = __builtin_e2k_qpfsubs(__builtin_e2k_qpfmuls(e_real, w_real), __builtin_e2k_qpfmuls(e_imag, w_imag)); __v2di ew_imag = __builtin_e2k_qpfadds(__builtin_e2k_qpfmuls(e_real, w_imag), __builtin_e2k_qpfmuls(e_imag, w_real)); __v2di add02_real = __builtin_e2k_qpfadds( x_real, dz_real); __v2di add02_imag = __builtin_e2k_qpfadds( x_imag, dz_imag); __v2di sub02_real = __builtin_e2k_qpfsubs( x_real, dz_real); __v2di sub02_imag = __builtin_e2k_qpfsubs( x_imag, dz_imag); __v2di add13_real = __builtin_e2k_qpfadds(cy_real, ew_real); __v2di add13_imag = __builtin_e2k_qpfadds(cy_imag, ew_imag); __v2di sub13_real = __builtin_e2k_qpfsubs(cy_real, ew_real); // __v2di sub13_imag = __builtin_e2k_qpfsubs(cy_imag, ew_imag); // __v2di sub13i_real = -sub13_imag; __v2di sub13i_real = __builtin_e2k_qpfsubs(ew_imag, cy_imag); __v2di sub13i_imag = sub13_real; out_0_real[2*i] = __builtin_e2k_qpfadds(add02_real, add13_real); out_0_imag[2*i] = __builtin_e2k_qpfadds(add02_imag, add13_imag); out_1_real[2*i] = __builtin_e2k_qpfsubs(sub02_real, sub13i_real); out_1_imag[2*i] = __builtin_e2k_qpfsubs(sub02_imag, sub13i_imag); out_2_real[2*i] = __builtin_e2k_qpfsubs(add02_real, add13_real); out_2_imag[2*i] = __builtin_e2k_qpfsubs(add02_imag, add13_imag); out_3_real[2*i] = __builtin_e2k_qpfadds(sub02_real, sub13i_real); out_3_imag[2*i] = __builtin_e2k_qpfadds(sub02_imag, sub13i_imag); } }
Основной цикл на ассемблере
.L7982: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=0, disp=32 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=2, disp=64 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=2, disp=96 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=4, disp=128 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=4, disp=160 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=6, disp=192 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=6, disp=224 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=8, disp=256 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=8, disp=288 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=1, abs=10, disp=320 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=12, disp=352 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=4, asz=2, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=4, asz=2, abs=16, disp=32 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=4, asz=2, abs=16, disp=64 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=3, asz=2, abs=20, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=3, asz=2, abs=20, disp=32 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=3, asz=2, abs=24, disp=64 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=2, asz=2, abs=24, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=2, asz=2, abs=28, disp=32 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=2, asz=2, abs=28, disp=64 } .L6334: { loop_mode qpfsub_rsubs,0,sm %b[105], %b[23], %g16, %g18 qpfsub_adds,1,sm %b[105], %b[23], %g16, %g17 qpfsubs,2,sm %g19, %g20, %g21 qpfmuls,3,sm %b[38], %b[113], %g24 qpfmuls,4,sm %b[46], %b[81], %g23 qpfmuls,5,sm %b[38], %b[64], %g22 } { loop_mode qpfadd_adds,0,sm %b[102], %b[49], %g25, %g27 qpfadd_rsubs,1,sm %b[102], %b[49], %g25, %g26 qpfadds,2,sm %g19, %g20, %g28 qpfmuls,3,sm %b[45], %b[109], %g31 qpfmuls,4,sm %b[42], %b[31], %g30 qpfmuls,5,sm %b[90], %b[94], %g29 } { loop_mode qpfsub_rsubs,0,sm %b[30], %r0, %r1, %r3 qpfsub_adds,1,sm %b[30], %r0, %r1, %r2 qpfmuls,2,sm %b[90], %b[97], %r7 qpfmuls,3,sm %b[48], %b[104], %b[0] qpfmuls,4,sm %b[82], %b[18], %r5 qpfmuls,5,sm %b[19], %b[76], %r4 } { loop_mode qpfadd_adds,0,sm %b[63], %r9, %r10, %r12 qpfadd_rsubs,1,sm %b[63], %r9, %r10, %r11 qpfmuls,2,sm %b[13], %b[93], %r14 qpfmuls,3,sm %b[11], %b[85], %r16 qpfmuls,4,sm %b[82], %b[70], %r15 qpfadds,5,sm %b[118], %b[119], %r13 } { loop_mode qpfsub_rsubs,0,sm %b[63], %r9, %g21, %g21 qpfsub_adds,1,sm %b[63], %r9, %g21, %r19 staaqp,2 %r18, %aad3[ %aasti7 ] qpfmuls,3,sm %b[17], %b[73], %b[118] qpfsubs,4,sm %b[119], %b[118], %r20 staaqp,5 %r17, %aad1[ %aasti5 ] } { loop_mode qpfadd_adds,0,sm %b[30], %r0, %g28, %g28 qpfadd_rsubs,1,sm %b[30], %r0, %g28, %r23 staaqp,2 %r22, %aad2[ %aasti6 + _f32s,_lts1 0x10 ] qpfmul_rsubs,3,sm %b[86], %b[97], %g29, %b[19] qpfsubs,4,sm %b[116], %r24, %r25 staaqp,5 %r21, %aad1[ %aasti5 + _f32s,_lts0 0x30 ] movaqp,0 area=10, ind=0, am=0, be=0, %b[1] movaqp,1 area=10, ind=16, am=1, be=0, %b[13] } { loop_mode qpfmul_adds,0,sm %b[12], %b[113], %g22, %b[49] qpfmul_adds,1,sm %b[86], %b[94], %r7, %b[45] staaqp,2 %r27, %aad3[ %aasti7 + _f32s,_lts1 0x30 ] qpfmul_rsubs,3,sm %b[27], %b[35], %g30, %r24 qpfadds,4,sm %b[116], %r24, %b[113] staaqp,5 %r26, %aad2[ %aasti6 + _f32s,_lts0 0x20 ] movaqp,0 area=9, ind=0, am=0, be=0, %b[30] movaqp,1 area=9, ind=16, am=1, be=0, %b[46] movaqp,2 area=9, ind=0, am=0, be=0, %b[23] movaqp,3 area=9, ind=16, am=1, be=0, %b[38] } { loop_mode qpfmul_adds,0,sm %b[34], %b[106], %r30, %b[117] qpfmul_adds,1,sm %b[41], %b[114], %g23, %b[116] staaqp,2 %b[117], %aad4[ %aasti8 + _f32s,_lts0 0x10 ] qpfmul_rsubs,3,sm %b[22], %b[109], %r31, %b[114] qpfsubs,4,sm %r29, %b[37], %g22 staaqp,5 %r28, %aad1[ %aasti5 + _f32s,_lts0 0x10 ] movaqp,0 area=8, ind=0, am=0, be=0, %b[82] movaqp,1 area=8, ind=16, am=1, be=0, %b[86] movaqp,2 area=8, ind=0, am=0, be=0, %b[63] movaqp,3 area=8, ind=16, am=1, be=0, %b[76] } { loop_mode qpfmul_rsubs,0,sm %b[7], %b[75], %r4, %g20 qpfmul_rsubs,1,sm %b[8], %b[87], %r14, %g19 staaqp,2 %r33, %aad3[ %aasti7 + _f32s,_lts1 0x10 ] qpfmul_rsubs,3,sm %b[34], %b[110], %b[2], %b[35] qpfmuls,4,sm %b[42], %b[35], %g23 staaqp,5 %r32, %aad4[ %aasti8 + _f32s,_lts0 0x20 ] movaqp,0 area=7, ind=0, am=0, be=0, %b[2] movaqp,1 area=7, ind=16, am=1, be=0, %b[7] movaqp,2 area=7, ind=0, am=0, be=0, %b[8] movaqp,3 area=7, ind=16, am=1, be=0, %b[34] } { loop_mode qpfmul_adds,0,sm %b[69], %b[70], %r5, %r9 qpfmul_rsubs,1,sm %b[69], %b[18], %r15, %r0 staaqp,2 %r35, %aad4[ %aasti8 ] qpfmul_rsubs,3,sm %b[41], %b[81], %r36, %r29 qpfadds,4,sm %r29, %b[37], %g29 staaqp,5 %r34, %aad2[ %aasti6 ] movaqp,0 area=6, ind=0, am=0, be=0, %b[37] movaqp,1 area=6, ind=16, am=1, be=0, %b[42] movaqp,2 area=6, ind=0, am=0, be=0, %b[18] movaqp,3 area=6, ind=16, am=1, be=0, %b[41] } { loop_mode qpfsub_rsubs,0,sm %b[54], %b[77], %r20, %r18 qpfsub_adds,1,sm %b[54], %b[77], %r20, %r17 staaqp,2 %g18, %aad3[ %aasti7 + _f32s,_lts0 0x20 ] qpfmul_rsubs,3,sm %b[12], %b[64], %g24, %b[75] qpfsubs,4,sm %r37, %r38, %g16 staaqp,5 %g17, %aad1[ %aasti5 + _f32s,_lts0 0x20 ] movaqp,0 area=5, ind=16, am=1, be=0, %b[64] movaqp,1 area=5, ind=0, am=0, be=0, %b[12] movaqp,2 area=5, ind=16, am=1, be=0, %b[70] movaqp,3 area=5, ind=0, am=0, be=0, %b[69] } { loop_mode qpfadd_rsubs,0,sm %b[60], %b[51], %r13, %r22 qpfsub_adds,1,sm %b[100], %b[47], %r25, %r21 staaqp,2 %g27, %aad4[ %aasti8 + _f32s,_lts0 0x30 ] qpfmul_adds,3,sm %b[22], %b[55], %g31, %r38 qpfadds,4,sm %r38, %r37, %g25 staaqp,5 %g26, %aad2[ %aasti6 + _f32s,_lts0 0x30 ] movaqp,0 area=4, ind=16, am=1, be=0, %b[55] movaqp,1 area=4, ind=0, am=0, be=0, %b[22] movaqp,2 area=4, ind=16, am=1, be=0, %b[87] movaqp,3 area=4, ind=0, am=0, be=0, %b[81] } { loop_mode qpfsub_rsubs,0,sm %b[100], %b[47], %r25, %r27 qpfadd_rsubs,1,sm %b[103], %b[21], %b[113], %r26 staaqp,2 %r3, %aad3[ %aasti7 + _f32s,_lts0 0x40 ] qpfmul_adds,3,sm %b[27], %b[31], %g23, %r37 qpfsubs,4,sm %b[115], %r39, %r1 staaqp,5 %r2, %aad1[ %aasti5 + _f32s,_lts0 0x40 ] movaqp,0 area=3, ind=0, am=0, be=0, %b[93] movaqp,1 area=3, ind=16, am=1, be=0, %b[90] movaqp,2 area=3, ind=0, am=0, be=0, %b[31] movaqp,3 area=3, ind=16, am=1, be=0, %b[27] } { loop_mode qpfadd_adds,0,sm %b[60], %b[51], %r13, %b[115] qpfsub_adds,1,sm %b[60], %b[51], %g22, %r28 staaqp,2 %r12, %aad4[ %aasti8 + _f32s,_lts0 0x50 ] qpfmul_adds,3,sm %b[6], %b[91], %r16, %r39 qpfadds,4,sm %r39, %b[115], %r10 staaqp,5 %r11, %aad2[ %aasti6 + _f32s,_lts0 0x50 ] movaqp,0 area=2, ind=0, am=0, be=0, %b[97] movaqp,1 area=2, ind=16, am=1, be=0, %b[94] movaqp,2 area=2, ind=0, am=0, be=0, %b[105] movaqp,3 area=1, ind=16, am=0, be=0, %b[102] } { loop_mode qpfsub_rsubs,0,sm %b[60], %b[51], %g22, %r33 qpfadd_adds,1,sm %b[103], %b[21], %b[113], %r32 staaqp,2 %g21, %aad3[ %aasti7 + _f32s,_lts0 0x50 ] incr,2 %aaincr3 qpfmul_adds,3,sm %b[5], %b[74], %b[118], %b[113] qpfmuls,4,sm %b[43], %b[53], %r31 staaqp,5 %r19, %aad1[ %aasti5 + _f32s,_lts0 0x50 ] incr,5 %aaincr3 movaqp,0 area=1, ind=16, am=1, be=0, %b[109] movaqp,1 area=1, ind=0, am=0, be=0, %b[60] movaqp,2 area=2, ind=16, am=1, be=0, %b[51] movaqp,3 area=1, ind=0, am=1, be=0, %b[106] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qpfadd_adds,0,sm %b[54], %b[77], %g29, %r35 qpfadd_rsubs,1,sm %b[54], %b[77], %g29, %r34 staaqp,2 %g28, %aad4[ %aasti8 + _f32s,_lts0 0x40 ] incr,2 %aaincr3 qpfmuls,3,sm %b[44], %b[112], %r36 qpfmuls,4,sm %b[48], %b[108], %r30 staaqp,5 %r23, %aad2[ %aasti6 + _f32s,_lts0 0x40 ] incr,5 %aaincr3 movaqp,0 area=0, ind=16, am=1, be=0, %b[54] movaqp,1 area=0, ind=0, am=0, be=0, %b[48] movaqp,2 area=0, ind=0, am=0, be=0, %b[77] movaqp,3 area=0, ind=16, am=1, be=0, %b[110] }
Теоретическая скорость: 48 комплексных чисел за 16 тактов (48/16) = 24 Байт/такт
Двойная теоретическая скорость: 48 Байт/такт
Замеры скорости

Компилятор не сумел векторизовать вариант stage_radix4_vector4_etalon в 4 раза.
До этого он успешно векторизовал вариант stage_radix4_vector2_etalon в 2 раза.
Итоги по stage_radix4_vector4


Пишем LastStage
В случае выполнения прямой векторизации в 2 раза нужно использовать lastStage_radix2.
В случае выполнения прямой векторизации в 4 раза нужно использовать lastStage_radix4 (или сначала stage_radix2_vector2 и затем lastStage_radix2).
lastStage_radix2
1. lastStage_radix2_etalon
Эталонный вариант для сравнения на корректность.
Код на Си
void lastStage_radix2_etalon(int data_count, myComplex2 *data_in, myComplex *data_out, myComplex *coef) { float *x_real_in = &data_in[0].real[0]; float *x_imag_in = &data_in[0].imag[0]; float *y_real_in = &data_in[0].real[1]; float *y_imag_in = &data_in[0].imag[1]; float *c_real_in = &coef[0].real; float *c_imag_in = &coef[0].imag; float *out_add_real = &data_out[0].real; float *out_add_imag = &data_out[0].imag; float *out_sub_real = &data_out[data_count/2].real; float *out_sub_imag = &data_out[data_count/2].imag; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < data_count/2; ++i) { float x_real = x_real_in[4*i]; float x_imag = x_imag_in[4*i]; float y_real = y_real_in[4*i]; float y_imag = y_imag_in[4*i]; float c_real = c_real_in[2*i]; float c_imag = c_imag_in[2*i]; float cy_real = c_real*y_real - c_imag*y_imag; float cy_imag = c_real*y_imag + c_imag*y_real; out_add_real[2*i] = x_real + cy_real; out_add_imag[2*i] = x_imag + cy_imag; out_sub_real[2*i] = x_real - cy_real; out_sub_imag[2*i] = x_imag - cy_imag; } }
Основной цикл на ассемблере
.L479: { fapb ct=1, dcd=0, fmt=3, mrng=16, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=3, mrng=8, d=0, incr=2, ind=2, asz=5, abs=0, disp=0 } .L182: { loop_mode fmuls,0,sm %b[67], %b[6], %b[37] fsubs,1,sm %b[46], %b[24], %b[58] staaw,2 %b[62], %aad1[ %aasti3 + _f32s,_lts0 0x4 ] fmul_adds,3,sm %b[55], %b[13], %b[43], %b[14] fadds,4,sm %b[46], %b[24], %b[57] staaw,5 %b[61], %aad2[ %aasti4 + _f32s,_lts0 0x4 ] movaw,0 area=0, ind=4, am=0, be=0, %b[0] movaw,1 area=0, ind=12, am=0, be=0, %b[1] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END fmuls,0,sm %b[67], %b[7], %b[62] fsubs,1,sm %b[35], %b[56], %b[70] staaw,2 %b[74], %aad1[ %aasti3 ] incr,2 %aaincr3 fmul_rsubs,3,sm %b[55], %b[12], %b[68], %b[46] fadds,4,sm %b[35], %b[56], %b[69] staaw,5 %b[73], %aad2[ %aasti4 ] incr,5 %aaincr3 movaw,0 area=0, ind=0, am=0, be=0, %b[13] movaw,1 area=0, ind=8, am=1, be=0, %b[24] movaw,2 area=0, ind=4, am=1, be=0, %b[61] movaw,3 area=0, ind=0, am=0, be=0, %b[43] }
Теоретическая скорость: 2 комплексных числа за 2 такта (2/2) = 8 Байт/такт
Замеры скорости

2. lastStage_radix2_etalon_unroll2
Здесь происходит раскрутка цикла в 2 раза с помощью директивы unroll.
Код на Си
void lastStage_radix2_etalon_unroll2(int data_count, myComplex2 *data_in, myComplex *data_out, myComplex *coef) { float *x_real_in = &data_in[0].real[0]; float *x_imag_in = &data_in[0].imag[0]; float *y_real_in = &data_in[0].real[1]; float *y_imag_in = &data_in[0].imag[1]; float *c_real_in = &coef[0].real; float *c_imag_in = &coef[0].imag; float *out_add_real = &data_out[0].real; float *out_add_imag = &data_out[0].imag; float *out_sub_real = &data_out[data_count/2].real; float *out_sub_imag = &data_out[data_count/2].imag; #pragma ivdep #pragma unroll(2) #pragma prefetch for(int64_t i = 0; i < data_count/2; ++i) { float x_real = x_real_in[4*i]; float x_imag = x_imag_in[4*i]; float y_real = y_real_in[4*i]; float y_imag = y_imag_in[4*i]; float c_real = c_real_in[2*i]; float c_imag = c_imag_in[2*i]; float cy_real = c_real*y_real - c_imag*y_imag; float cy_imag = c_real*y_imag + c_imag*y_real; out_add_real[2*i] = x_real + cy_real; out_add_imag[2*i] = x_imag + cy_imag; out_sub_real[2*i] = x_real - cy_real; out_sub_imag[2*i] = x_imag - cy_imag; } }
Основной цикл на ассемблере
.L1357: { fapb ct=0, dcd=0, fmt=3, mrng=12, d=0, incr=1, ind=1, asz=4, abs=0, disp=20 fapb dpl=0, dcd=0, fmt=3, mrng=20, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 } { fapb ct=1, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=4, abs=16, disp=0 } .L525: { loop_mode pfmuls,0,sm %b[56], %b[16], %b[26] insfd,1,sm %b[52], %r8, %b[47], %b[1] pfmuls,2,sm %b[56], %b[13], %b[31] pshufb,3,sm %b[19], %b[9], %r0, %b[57] insfd,4,sm %b[55], %r8, %b[37], %b[0] pfadds,5,sm %b[42], %b[43], %b[10] } { loop_mode pfmul_rsubs,0,sm %b[5], %b[15], %b[28], %b[42] insfd,1,sm %b[27], %r8, %b[22], %b[32] pfmul_adds,2,sm %b[5], %b[18], %b[33], %b[37] pshufb,4,sm %b[12], %b[23], %r0, %b[55] staad,5 %b[57], %aad1[ %aasti3 + _f32s,_lts0 0x8 ] movad,0 area=1, ind=0, am=1, be=0, %b[43] movad,1 area=1, ind=8, am=0, be=0, %b[48] movaw,3 area=0, ind=12, am=0, be=0, %b[47] } { loop_mode pfsubs,0,sm %b[8], %b[46], %b[5] insfd,1,sm %b[19], %r8, %b[9], %b[58] pfsubs,2,sm %b[40], %b[41], %b[15] insfd,4,sm %b[12], %r8, %b[23], %b[56] staad,5 %b[55], %aad2[ %aasti4 + _f32s,_lts0 0x8 ] movaw,0 area=0, ind=8, am=0, be=0, %b[22] movaw,1 area=0, ind=0, am=0, be=0, %b[28] movaw,2 area=0, ind=8, am=0, be=0, %b[18] movaw,3 area=0, ind=4, am=0, be=0, %b[27] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END pfadds,0,sm %b[8], %b[46], %b[19] insfd,1,sm %b[30], %r8, %b[29], %b[9] staad,2 %b[58], %aad1[ %aasti3 ] incr,2 %aaincr3 pshufb,3,sm %b[50], %b[45], %r0, %b[52] insfd,4,sm %b[24], %r8, %b[49], %b[12] staad,5 %b[56], %aad2[ %aasti4 ] incr,5 %aaincr3 movaw,1 area=0, ind=4, am=1, be=0, %b[23] movaw,2 area=0, ind=0, am=1, be=0, %b[33] movaw,3 area=0, ind=16, am=0, be=0, %b[51] }
Теоретическая скорость: 4 комплексных числа за 4 такта (4/4) = 8 Байт/такт
Замеры скорости

3. lastStage_radix2_simd64
Модифицированный stage_radix2_simd64 для работы с векторными данными.
Код на Си
void lastStage_radix2_simd64(int data_count, myComplex2 *data_in, myComplex *data_out, myComplex *coef) { uint64_t *xy0_real_in = (uint64_t*)&data_in[0].real; uint64_t *xy0_imag_in = (uint64_t*)&data_in[0].imag; uint64_t *xy1_real_in = (uint64_t*)&data_in[1].real; uint64_t *xy1_imag_in = (uint64_t*)&data_in[1].imag; uint64_t *c0_in = (uint64_t*)&coef[0]; uint64_t *c1_in = (uint64_t*)&coef[1]; uint64_t *out_add0 = (uint64_t*)&data_out[0]; uint64_t *out_add1 = (uint64_t*)&data_out[1]; uint64_t *out_sub0 = (uint64_t*)&data_out[data_count/2 + 0]; uint64_t *out_sub1 = (uint64_t*)&data_out[data_count/2 + 1]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { uint64_t xy0_real = xy0_real_in[4*i]; uint64_t xy0_imag = xy0_imag_in[4*i]; uint64_t xy1_real = xy1_real_in[4*i]; uint64_t xy1_imag = xy1_imag_in[4*i]; uint64_t c0 = c0_in[2*i]; uint64_t c1 = c1_in[2*i]; uint64_t x_real = __builtin_e2k_pshufb(xy1_real, xy0_real, 0x0B0A090803020100); uint64_t x_imag = __builtin_e2k_pshufb(xy1_imag, xy0_imag, 0x0B0A090803020100); uint64_t y_real = __builtin_e2k_pshufb(xy1_real, xy0_real, 0x0F0E0D0C07060504); uint64_t y_imag = __builtin_e2k_pshufb(xy1_imag, xy0_imag, 0x0F0E0D0C07060504); uint64_t c_real = __builtin_e2k_pshufb(c1, c0, 0x0B0A090803020100); uint64_t c_imag = __builtin_e2k_pshufb(c1, c0, 0x0F0E0D0C07060504); uint64_t cy_real = __builtin_e2k_pfsubs(__builtin_e2k_pfmuls(c_real, y_real), __builtin_e2k_pfmuls(c_imag, y_imag)); uint64_t cy_imag = __builtin_e2k_pfadds(__builtin_e2k_pfmuls(c_real, y_imag), __builtin_e2k_pfmuls(c_imag, y_real)); uint64_t add_real = __builtin_e2k_pfadds(x_real, cy_real); uint64_t add_imag = __builtin_e2k_pfadds(x_imag, cy_imag); uint64_t sub_real = __builtin_e2k_pfsubs(x_real, cy_real); uint64_t sub_imag = __builtin_e2k_pfsubs(x_imag, cy_imag); out_add0[2*i] = __builtin_e2k_pshufb(add_imag, add_real, 0x0B0A090803020100); out_add1[2*i] = __builtin_e2k_pshufb(add_imag, add_real, 0x0F0E0D0C07060504); out_sub0[2*i] = __builtin_e2k_pshufb(sub_imag, sub_real, 0x0B0A090803020100); out_sub1[2*i] = __builtin_e2k_pshufb(sub_imag, sub_real, 0x0F0E0D0C07060504); } }
Основной цикл на ассемблере
.L1929: { fapb ct=1, dcd=0, fmt=4, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=5, abs=0, disp=0 } .L1613: { loop_mode pfadds,0,sm %b[26], %b[50], %b[68] pshufb,1,sm %b[58], %b[61], %r6, %b[1] pfadds,2,sm %b[10], %b[49], %b[9] pshufb,3,sm %b[43], %b[44], %r5, %b[0] pshufb,4,sm %b[71], %b[57], %r6, %b[72] pfsubs,5,sm %b[10], %b[49], %b[69] } { loop_mode pshufb,0,sm %b[53], %b[54], %r5, %b[10] pfmul_adds,1,sm %b[34], %b[66], %b[33], %b[43] pfmul_rsubs,2,sm %b[34], %b[23], %b[67], %b[44] pshufb,3,sm %b[60], %b[63], %r5, %b[26] pshufb,4,sm %b[71], %b[57], %r5, %b[73] staad,5 %b[72], %aad1[ %aasti3 + _f32s,_lts0 0x8 ] movad,0 area=0, ind=0, am=0, be=0, %b[50] movad,1 area=0, ind=16, am=0, be=0, %b[49] } { loop_mode pfmuls,0,sm %b[1], %b[13], %b[23] pshufb,1,sm %b[11], %b[70], %r6, %b[71] pfsubs,2,sm %b[24], %b[48], %b[53] pshufb,4,sm %b[11], %b[70], %r5, %b[72] staad,5 %b[73], %aad1[ %aasti3 ] incr,5 %aaincr3 movad,0 area=0, ind=8, am=1, be=0, %b[34] movad,1 area=0, ind=24, am=0, be=0, %b[33] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END pshufb,0,sm %b[51], %b[52], %r6, %b[11] pfmuls,1,sm %b[7], %b[62], %b[63] staad,2 %b[71], %aad2[ %aasti4 + _f32s,_lts0 0x8 ] pshufb,4,sm %b[41], %b[42], %r6, %b[60] staad,5 %b[72], %aad2[ %aasti4 ] incr,5 %aaincr3 movad,2 area=0, ind=0, am=0, be=0, %b[57] movad,3 area=0, ind=8, am=1, be=0, %b[54] }
Теоретическая скорость: 4 комплексных числа за 4 такта (4/4) = 8 Байт/такт
Замеры скорости

4. lastStage_radix2_simd128_noConj
Модифицированный stage_radix2_simd128_noConj для работы с векторными данными.
Код на Си
void lastStage_radix2_simd128_noConj(int data_count, myComplex2 *data_in, myComplex *data_out, myComplex *coef) { __v2di *xy0_in = (__v2di*)&data_in[0]; __v2di *xy1_in = (__v2di*)&data_in[1]; __v2di *c_in = (__v2di*)coef; __v2di *out_add = (__v2di*)&data_out[0]; __v2di *out_sub = (__v2di*)&data_out[data_count/2]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { __v2di xy0 = xy0_in[2*i]; __v2di xy1 = xy1_in[2*i]; __v2di c = c_in[i]; __v2di x = __builtin_e2k_qpshufb(xy1, xy0, (__v2di){0x0B0A090803020100, 0x0B0A090803020100}); __v2di y = __builtin_e2k_qpshufb(xy1, xy0, (__v2di){0x0F0E0D0C07060504, 0x0F0E0D0C07060504}); __v2di swap_c = __builtin_e2k_qpshufb(c, c, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di cy_real = __builtin_e2k_qpfmuls( c, y); __v2di cy_imag = __builtin_e2k_qpfmuls(swap_c, y); __v2di cy_rr = __builtin_e2k_qpfhsubs((__v2di){0}, cy_real); __v2di cy_ii = __builtin_e2k_qpfhadds((__v2di){0}, cy_imag); __v2di cy = __builtin_e2k_qppermb(cy_ii, cy_rr, (__v2di){0x1B1A19180B0A0908, 0x1F1E1D1C0F0E0D0C}); out_add[i] = __builtin_e2k_qpfadds(x, cy); out_sub[i] = __builtin_e2k_qpfsubs(x, cy); } }
Основной цикл на ассемблере
.L2215: { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=2, asz=5, abs=0, disp=0 } .L1954: { loop_mode qpfmul_hsubs,0,sm %b[25], %b[28], %r9, %b[16] qpfmul_hadds,1,sm %b[27], %b[28], %r9, %b[1] qpfsubs,2,sm %b[14], %b[42], %b[37] qpshufb,3,sm %b[35], %b[36], %r6, %b[0] qppermb,4,sm %b[11], %b[26], %r7, %b[38] staaqp,5 %b[43], %aad1[ %aasti3 ] incr,5 %aaincr0 movaqp,0 area=0, ind=0, am=0, be=0, %b[30] movaqp,1 area=0, ind=16, am=1, be=0, %b[29] movaqp,3 area=0, ind=0, am=1, be=0, %b[19] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qpshufb,0,sm %b[33], %b[34], %r0, %b[26] qpshufb,1,sm %b[23], %b[23], %r5, %b[25] qpfadds,2,sm %b[14], %b[42], %b[11] staaqp,5 %b[17], %aad2[ %aasti4 ] incr,5 %aaincr0 }
Теоретическая скорость: 4 комплексных числа за 2 такта (4/2) = 16 Байт/такт
Замеры скорости

Итоги по lastStage_radix2


При реализации Vector-2 FFT будем использовать lastStage_radix2_simd128_noConj.
lastStage_radix4
Один проход по lastStage_radix4 совершает ту же работу, что 2 прохода по stage_radix2. Поэтому скорость lastStage_radix4 будем умножать на 2 для удобства сравнения с stage_radix2 (этот факт подписан на оси графика и в выводе консоли).
1. lastStage_radix4_etalon
Эталонный вариант для сравнения на корректность.
Код на Си
void lastStage_radix4_etalon(int data_count, myComplex4 *data_in, myComplex *data_out, myComplex *coefC, myComplex *coefD, myComplex *coefE) { float *x_real_in = &data_in[0].real[0]; float *x_imag_in = &data_in[0].imag[0]; float *y_real_in = &data_in[0].real[1]; float *y_imag_in = &data_in[0].imag[1]; float *z_real_in = &data_in[0].real[2]; float *z_imag_in = &data_in[0].imag[2]; float *w_real_in = &data_in[0].real[3]; float *w_imag_in = &data_in[0].imag[3]; float *c_real_in = &coefC[0].real; float *c_imag_in = &coefC[0].imag; float *d_real_in = &coefD[0].real; float *d_imag_in = &coefD[0].imag; float *e_real_in = &coefE[0].real; float *e_imag_in = &coefE[0].imag; float *out_0_real = &data_out[0*data_count/4].real; float *out_0_imag = &data_out[0*data_count/4].imag; float *out_1_real = &data_out[1*data_count/4].real; float *out_1_imag = &data_out[1*data_count/4].imag; float *out_2_real = &data_out[2*data_count/4].real; float *out_2_imag = &data_out[2*data_count/4].imag; float *out_3_real = &data_out[3*data_count/4].real; float *out_3_imag = &data_out[3*data_count/4].imag; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { float x_real = x_real_in[8*i]; float x_imag = x_imag_in[8*i]; float y_real = y_real_in[8*i]; float y_imag = y_imag_in[8*i]; float z_real = z_real_in[8*i]; float z_imag = z_imag_in[8*i]; float w_real = w_real_in[8*i]; float w_imag = w_imag_in[8*i]; float c_real = c_real_in[2*i]; float c_imag = c_imag_in[2*i]; float d_real = d_real_in[2*i]; float d_imag = d_imag_in[2*i]; float e_real = e_real_in[2*i]; float e_imag = e_imag_in[2*i]; float cy_real = c_real*y_real - c_imag*y_imag; float cy_imag = c_real*y_imag + c_imag*y_real; float dz_real = d_real*z_real - d_imag*z_imag; float dz_imag = d_real*z_imag + d_imag*z_real; float ew_real = e_real*w_real - e_imag*w_imag; float ew_imag = e_real*w_imag + e_imag*w_real; float add02_real = x_real + dz_real; float add02_imag = x_imag + dz_imag; float sub02_real = x_real - dz_real; float sub02_imag = x_imag - dz_imag; float add13_real = cy_real + ew_real; float add13_imag = cy_imag + ew_imag; float sub13_real = cy_real - ew_real; float sub13_imag = cy_imag - ew_imag; float sub13i_real = -sub13_imag; float sub13i_imag = sub13_real; out_0_real[2*i] = add02_real + add13_real; out_0_imag[2*i] = add02_imag + add13_imag; out_1_real[2*i] = sub02_real - sub13i_real; out_1_imag[2*i] = sub02_imag - sub13i_imag; out_2_real[2*i] = add02_real - add13_real; out_2_imag[2*i] = add02_imag - add13_imag; out_3_real[2*i] = sub02_real + sub13i_real; out_3_imag[2*i] = sub02_imag + sub13i_imag; } }
Основной цикл на ассемблере
.L973: { fapb ct=0, dcd=0, fmt=3, mrng=8, d=0, incr=2, ind=4, asz=3, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=3, mrng=0, d=0, incr=1, ind=1, asz=5, abs=0, disp=0 } { fapb ct=0, dcd=0, fmt=3, mrng=8, d=0, incr=2, ind=3, asz=3, abs=8, disp=0 } { fapb ct=1, dcd=0, fmt=3, mrng=8, d=0, incr=2, ind=2, asz=4, abs=16, disp=0 } .L394: { loop_mode fmul_adds,0,sm %b[66], %b[72], %b[54], %b[1] fsub_rsubs,1,sm %b[12], %b[71], %b[55], %b[5] fsub_adds,2,sm %b[12], %b[71], %b[55], %b[45] fmuls,4,sm %b[59], %b[58], %b[52] fsubs,5,sm %b[79], %b[75], %b[53] movaw,3 area=0, ind=16, am=0, be=0, %b[0] } { loop_mode fmul_rsubs,0,sm %b[66], %b[60], %b[82], %b[71] fadd_adds,1,sm %b[24], %b[83], %b[90], %b[72] fadd_rsubs,2,sm %b[24], %b[83], %b[90], %b[76] fmuls,4,sm %b[59], %b[70], %b[80] fadds,5,sm %b[79], %b[75], %b[88] movaw,1 area=2, ind=4, am=0, be=0, %b[55] movaw,2 area=0, ind=0, am=0, be=0, %b[12] movaw,3 area=0, ind=12, am=0, be=0, %b[54] } { loop_mode fmul_rsubs,0,sm %b[42], %b[17], %b[84], %b[75] fmul_rsubs,1,sm %b[32], %b[67], %b[85], %b[79] staaw,2 %b[36], %aad3[ %aasti7 ] fmuls,3,sm %b[29], %b[46], %b[82] fmuls,4,sm %b[35], %b[23], %b[83] staaw,5 %b[39], %aad1[ %aasti5 ] movaw,0 area=2, ind=0, am=1, be=0, %b[60] movaw,1 area=1, ind=0, am=0, be=0, %b[24] movaw,2 area=0, ind=8, am=0, be=0, %b[59] movaw,3 area=0, ind=28, am=0, be=0, %b[66] } { loop_mode fmul_adds,0,sm %b[40], %b[46], %b[86], %b[39] fmul_adds,1,sm %b[32], %b[25], %b[87], %b[67] staaw,2 %b[11], %aad4[ %aasti8 + _f32s,_lts0 0x4 ] fmuls,3,sm %b[27], %b[13], %b[84] fmuls,4,sm %b[35], %b[65], %b[85] staaw,5 %b[51], %aad2[ %aasti6 + _f32s,_lts0 0x4 ] movaw,0 area=1, ind=4, am=1, be=0, %b[29] movaw,1 area=0, ind=0, am=0, be=0, %b[36] movaw,2 area=0, ind=24, am=0, be=0, %b[17] movaw,3 area=0, ind=20, am=0, be=0, %b[42] } { loop_mode fsub_rsubs,0,sm %b[22], %b[81], %b[48], %b[32] fsub_adds,1,sm %b[22], %b[81], %b[48], %b[35] staaw,2 %b[7], %aad3[ %aasti7 + _f32s,_lts0 0x4 ] incr,2 %aaincr3 fsubs,4,sm %b[3], %b[43], %b[46] staaw,5 %b[47], %aad1[ %aasti5 + _f32s,_lts0 0x4 ] incr,5 %aaincr3 movaw,1 area=0, ind=4, am=1, be=0, %b[25] movaw,3 area=0, ind=4, am=1, be=0, %b[11] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END fadd_adds,0,sm %b[10], %b[69], %b[50], %b[7] fadd_rsubs,1,sm %b[10], %b[69], %b[50], %b[47] staaw,2 %b[74], %aad4[ %aasti8 ] incr,2 %aaincr3 fadds,4,sm %b[43], %b[3], %b[48] staaw,5 %b[78], %aad2[ %aasti6 ] incr,5 %aaincr3 }
Теоретическая скорость: 4 комплексных числа за 6 тактов (4/6) = 5.33 Байт/такт
Двойная теоретическая скорость: 10.67 Байт/такт
Замеры скорости

2. lastStage_radix4_etalon_unroll2
Здесь происходит раскрутка цикла в 2 раза с помощью директивы unroll.
Код на Си
void lastStage_radix4_etalon_unroll2(int data_count, myComplex4 *data_in, myComplex *data_out, myComplex *coefC, myComplex *coefD, myComplex *coefE) { float *x_real_in = &data_in[0].real[0]; float *x_imag_in = &data_in[0].imag[0]; float *y_real_in = &data_in[0].real[1]; float *y_imag_in = &data_in[0].imag[1]; float *z_real_in = &data_in[0].real[2]; float *z_imag_in = &data_in[0].imag[2]; float *w_real_in = &data_in[0].real[3]; float *w_imag_in = &data_in[0].imag[3]; float *c_real_in = &coefC[0].real; float *c_imag_in = &coefC[0].imag; float *d_real_in = &coefD[0].real; float *d_imag_in = &coefD[0].imag; float *e_real_in = &coefE[0].real; float *e_imag_in = &coefE[0].imag; float *out_0_real = &data_out[0*data_count/4].real; float *out_0_imag = &data_out[0*data_count/4].imag; float *out_1_real = &data_out[1*data_count/4].real; float *out_1_imag = &data_out[1*data_count/4].imag; float *out_2_real = &data_out[2*data_count/4].real; float *out_2_imag = &data_out[2*data_count/4].imag; float *out_3_real = &data_out[3*data_count/4].real; float *out_3_imag = &data_out[3*data_count/4].imag; #pragma ivdep #pragma unroll(2) #pragma prefetch for(int64_t i = 0; i < data_count/4; ++i) { float x_real = x_real_in[8*i]; float x_imag = x_imag_in[8*i]; float y_real = y_real_in[8*i]; float y_imag = y_imag_in[8*i]; float z_real = z_real_in[8*i]; float z_imag = z_imag_in[8*i]; float w_real = w_real_in[8*i]; float w_imag = w_imag_in[8*i]; float c_real = c_real_in[2*i]; float c_imag = c_imag_in[2*i]; float d_real = d_real_in[2*i]; float d_imag = d_imag_in[2*i]; float e_real = e_real_in[2*i]; float e_imag = e_imag_in[2*i]; float cy_real = c_real*y_real - c_imag*y_imag; float cy_imag = c_real*y_imag + c_imag*y_real; float dz_real = d_real*z_real - d_imag*z_imag; float dz_imag = d_real*z_imag + d_imag*z_real; float ew_real = e_real*w_real - e_imag*w_imag; float ew_imag = e_real*w_imag + e_imag*w_real; float add02_real = x_real + dz_real; float add02_imag = x_imag + dz_imag; float sub02_real = x_real - dz_real; float sub02_imag = x_imag - dz_imag; float add13_real = cy_real + ew_real; float add13_imag = cy_imag + ew_imag; float sub13_real = cy_real - ew_real; float sub13_imag = cy_imag - ew_imag; float sub13i_real = -sub13_imag; float sub13i_imag = sub13_real; out_0_real[2*i] = add02_real + add13_real; out_0_imag[2*i] = add02_imag + add13_imag; out_1_real[2*i] = sub02_real - sub13i_real; out_1_imag[2*i] = sub02_imag - sub13i_imag; out_2_real[2*i] = add02_real - add13_real; out_2_imag[2*i] = add02_imag - add13_imag; out_3_real[2*i] = sub02_real + sub13i_real; out_3_imag[2*i] = sub02_imag + sub13i_imag; } }
Основной цикл на ассемблере
.L2296: { fapb ct=0, dcd=0, fmt=3, mrng=0, d=0, incr=1, ind=1, asz=3, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=3, mrng=0, d=0, incr=1, ind=1, asz=4, abs=0, disp=32 } { fapb ct=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=4, asz=3, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=3, asz=4, abs=16, disp=0 } { fapb ct=1, dcd=0, fmt=4, mrng=16, d=0, incr=2, ind=2, asz=4, abs=16, disp=0 } .L1081: { loop_mode pfmul_rsubs,0,sm %g17, %g18, %g19, %b[62] pshufb,1,sm %b[90], %b[87], %r0, %g16 pfmul_adds,2,sm %g17, %b[101], %b[117], %b[73] insfd,3,sm %b[107], %r7, %b[98], %b[27] insfd,4,sm %b[49], %r7, %b[97], %b[49] pfmuls,5,sm %b[91], %b[29], %b[117] movad,0 area=1, ind=0, am=0, be=0, %b[1] movad,1 area=1, ind=8, am=1, be=0, %b[0] } { loop_mode pfsub_adds,0,sm %b[118], %b[64], %b[108], %b[85] pfmuls,1,sm %b[115], %b[99], %g19 pfsub_adds,2,sm %b[119], %b[75], %g20, %b[88] insfd,3,sm %b[32], %r7, %b[37], %b[92] insfd,4,sm %b[78], %r7, %b[68], %b[81] pfadds,5,sm %b[28], %b[33], %b[91] movad,0 area=2, ind=0, am=0, be=0, %b[78] movad,1 area=2, ind=8, am=1, be=0, %b[68] movad,2 area=1, ind=0, am=0, be=0, %b[37] movad,3 area=1, ind=8, am=1, be=0, %b[32] } { loop_mode pfadd_rsubs,0,sm %b[118], %b[64], %b[93], %b[98] pfsubs,1,sm %b[55], %b[44], %b[106] pfadd_rsubs,2,sm %b[119], %b[75], %b[113], %b[102] insfd,3,sm %b[50], %r7, %b[61], %b[97] insfd,4,sm %b[15], %r7, %b[14], %g18 pfmuls,5,sm %b[114], %b[81], %b[101] movaw,0 area=0, ind=0, am=0, be=0, %b[15] movaw,1 area=0, ind=16, am=0, be=0, %b[61] movaw,2 area=0, ind=0, am=0, be=0, %b[50] movaw,3 area=0, ind=16, am=0, be=0, %b[14] } { loop_mode pfsub_rsubs,0,sm %b[118], %b[64], %b[108], %b[107] pfadds,1,sm %b[44], %b[55], %b[111] pfsub_rsubs,2,sm %b[119], %b[75], %g20, %b[108] insfd,3,sm %b[6], %r7, %b[7], %g22 pshufb,4,sm %b[104], %b[100], %r0, %g21 pfmuls,5,sm %b[115], %g18, %b[115] movaw,0 area=0, ind=8, am=0, be=0, %b[6] movaw,1 area=0, ind=24, am=0, be=0, %b[55] movaw,2 area=0, ind=8, am=0, be=0, %b[7] movaw,3 area=0, ind=24, am=0, be=0, %b[44] } { loop_mode pfadd_adds,0,sm %b[118], %b[64], %b[93], %b[75] pshufb,1,sm %b[38], %b[43], %r0, %b[113] pfadd_adds,2,sm %b[119], %b[75], %b[113], %b[84] insfd,3,sm %b[74], %r7, %b[84], %g24 pshufb,4,sm %b[110], %b[109], %r0, %g23 pfsubs,5,sm %b[28], %b[33], %g20 movaw,0 area=0, ind=20, am=0, be=0, %b[33] movaw,1 area=0, ind=4, am=0, be=0, %b[64] movaw,2 area=0, ind=20, am=0, be=0, %b[28] movaw,3 area=0, ind=4, am=0, be=0, %b[74] } { loop_mode pfmul_adds,0,sm %g22, %b[94], %b[103], %b[40] pshufb,1,sm %b[2], %b[3], %r0, %b[112] staad,2 %g16, %aad1[ %aasti5 + _f32s,_lts0 0x8 ] insfd,3,sm %b[40], %r7, %b[45], %g17 pshufb,4,sm %b[86], %b[77], %r0, %g16 staad,5 %g23, %aad3[ %aasti7 + _f32s,_lts0 0x8 ] movaw,0 area=0, ind=12, am=1, be=0, %b[94] movaw,1 area=0, ind=28, am=0, be=0, %b[93] movaw,2 area=0, ind=12, am=1, be=0, %b[103] movaw,3 area=0, ind=28, am=0, be=0, %b[45] } { loop_mode pfmul_adds,0,sm %g24, %b[51], %b[117], %b[51] pshufb,1,sm %b[70], %b[80], %r0, %b[87] staad,2 %g21, %aad2[ %aasti6 + _f32s,_lts0 0x8 ] insfd,3,sm %b[90], %r7, %b[87], %b[117] insfd,4,sm %b[110], %r7, %b[109], %g16 staad,5 %g16, %aad4[ %aasti8 + _f32s,_lts0 0x8 ] } { loop_mode pfmul_rsubs,0,sm %g24, %b[29], %b[116], %b[29] pfmuls,1,sm %b[114], %b[92], %b[77] staad,2 %b[117], %aad1[ %aasti5 ] incr,2 %aaincr3 insfd,3,sm %b[86], %r7, %b[77], %b[114] insfd,4,sm %b[104], %r7, %b[100], %b[116] staad,5 %g16, %aad3[ %aasti7 ] incr,5 %aaincr3 } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END pfmul_rsubs,0,sm %g22, %b[83], %b[79], %b[24] pfmuls,1,sm %b[89], %b[49], %b[114] staad,2 %b[114], %aad4[ %aasti8 ] incr,2 %aaincr3 insfd,3,sm %b[60], %r7, %b[25], %b[116] insfd,4,sm %b[24], %r7, %b[71], %b[117] staad,5 %b[116], %aad2[ %aasti6 ] incr,5 %aaincr3 }
Теоретическая скорость: 8 комплексных чисел за 9 тактов (8/9) = 7.11 Байт/такт
Двойная теоретическая скорость: 14.22 Байт/такт
Замеры скорости

3. lastStage_radix4_simd128_noConj
Модифицированный stage_radix4_simd128_noConj для работы с векторными данными.
Код на Си
void lastStage_radix4_simd128_noConj(int data_count, myComplex4 *data_in, myComplex *data_out, myComplex *coefC, myComplex *coefD, myComplex *coefE) { __v2di *xyzw0_real_in = (__v2di*)&data_in[0].real; __v2di *xyzw0_imag_in = (__v2di*)&data_in[0].imag; __v2di *xyzw1_real_in = (__v2di*)&data_in[1].real; __v2di *xyzw1_imag_in = (__v2di*)&data_in[1].imag; __v2di *c_in = (__v2di*)coefC; __v2di *d_in = (__v2di*)coefD; __v2di *e_in = (__v2di*)coefE; __v2di *out_0 = (__v2di*)&data_out[0*data_count/4]; __v2di *out_1 = (__v2di*)&data_out[1*data_count/4]; __v2di *out_2 = (__v2di*)&data_out[2*data_count/4]; __v2di *out_3 = (__v2di*)&data_out[3*data_count/4]; #pragma ivdep #pragma unroll(1) #pragma prefetch for(int64_t i = 0; i < data_count/8; ++i) { __v2di xyzw0_real = xyzw0_real_in[4*i]; __v2di xyzw0_imag = xyzw0_imag_in[4*i]; __v2di xyzw1_real = xyzw1_real_in[4*i]; __v2di xyzw1_imag = xyzw1_imag_in[4*i]; __v2di c = c_in[i]; __v2di d = d_in[i]; __v2di e = e_in[i]; __v2di xy_real = __builtin_e2k_qpshufb(xyzw1_real, xyzw0_real, (__v2di){0x0706050403020100, 0x0706050403020100}); __v2di zw_real = __builtin_e2k_qpshufb(xyzw1_real, xyzw0_real, (__v2di){0x0F0E0D0C0B0A0908, 0x0F0E0D0C0B0A0908}); __v2di xy_imag = __builtin_e2k_qpshufb(xyzw1_imag, xyzw0_imag, (__v2di){0x0706050403020100, 0x0706050403020100}); __v2di zw_imag = __builtin_e2k_qpshufb(xyzw1_imag, xyzw0_imag, (__v2di){0x0F0E0D0C0B0A0908, 0x0F0E0D0C0B0A0908}); __v2di x = __builtin_e2k_qppermb(xy_imag, xy_real, (__v2di){0x1312111003020100, 0x1B1A19180B0A0908}); __v2di y = __builtin_e2k_qppermb(xy_imag, xy_real, (__v2di){0x1716151407060504, 0x1F1E1D1C0F0E0D0C}); __v2di z = __builtin_e2k_qppermb(zw_imag, zw_real, (__v2di){0x1312111003020100, 0x1B1A19180B0A0908}); __v2di w = __builtin_e2k_qppermb(zw_imag, zw_real, (__v2di){0x1716151407060504, 0x1F1E1D1C0F0E0D0C}); __v2di swap_c = __builtin_e2k_qpshufb(c, c, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di swap_d = __builtin_e2k_qpshufb(d, d, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di swap_e = __builtin_e2k_qpshufb(e, e, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di cy_real = __builtin_e2k_qpfmuls( c, y); __v2di dz_real = __builtin_e2k_qpfmuls( d, z); __v2di ew_real = __builtin_e2k_qpfmuls( e, w); __v2di cy_imag = __builtin_e2k_qpfmuls(swap_c, y); __v2di dz_imag = __builtin_e2k_qpfmuls(swap_d, z); __v2di ew_imag = __builtin_e2k_qpfmuls(swap_e, w); __v2di cy_rr = __builtin_e2k_qpfhsubs((__v2di){0}, cy_real); __v2di dz_rr = __builtin_e2k_qpfhsubs((__v2di){0}, dz_real); __v2di ew_rr = __builtin_e2k_qpfhsubs((__v2di){0}, ew_real); __v2di cy_ii = __builtin_e2k_qpfhadds((__v2di){0}, cy_imag); __v2di dz_ii = __builtin_e2k_qpfhadds((__v2di){0}, dz_imag); __v2di ew_ii = __builtin_e2k_qpfhadds((__v2di){0}, ew_imag); __v2di cy = __builtin_e2k_qppermb(cy_ii, cy_rr, (__v2di){0x1B1A19180B0A0908, 0x1F1E1D1C0F0E0D0C}); __v2di dz = __builtin_e2k_qppermb(dz_ii, dz_rr, (__v2di){0x1B1A19180B0A0908, 0x1F1E1D1C0F0E0D0C}); __v2di ew = __builtin_e2k_qppermb(ew_ii, ew_rr, (__v2di){0x1B1A19180B0A0908, 0x1F1E1D1C0F0E0D0C}); __v2di add02 = __builtin_e2k_qpfadds( x, dz); __v2di sub02 = __builtin_e2k_qpfsubs( x, dz); __v2di add13 = __builtin_e2k_qpfadds(cy, ew); __v2di sub13 = __builtin_e2k_qpfsubs(cy, ew); //__v2di conj_sub13 = __builtin_e2k_qpxor(sub13, (__v2di){1LL<<63, 1LL<<63}); //__v2di sub13i = __builtin_e2k_qpshufb(conj_sub13, conj_sub13, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di swap_sub13 = __builtin_e2k_qpshufb(sub13, sub13, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di sub13i = __builtin_e2k_qpxor(swap_sub13, (__v2di){1LL<<31, 1LL<<31}); out_0[i] = __builtin_e2k_qpfadds(add02, add13); out_1[i] = __builtin_e2k_qpfsubs(sub02, sub13i); out_2[i] = __builtin_e2k_qpfsubs(add02, add13); out_3[i] = __builtin_e2k_qpfadds(sub02, sub13i); } }
Основной цикл на ассемблере
.L2983: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=3, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=4, abs=0, disp=32 } { fapb ct=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=4, asz=3, abs=8, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=3, asz=4, abs=16, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=0, ind=2, asz=4, abs=16, disp=0 } .L2350: { loop_mode qpfmul_hsubs,0,sm %b[21], %b[49], %r14, %b[4] qpshufb,1,sm %b[21], %b[21], %r10, %b[11] qpfsub_rsubs,2,sm %b[42], %b[56], %b[54], %b[1] qppermb,4,sm %b[24], %b[25], %r12, %b[9] qpfadds,5,sm %b[48], %b[45], %b[0] } { loop_mode qpfmul_hadds,0,sm %b[11], %b[49], %r14, %b[21] qppermb,1,sm %b[51], %b[55], %r12, %b[33] qpfsub_adds,2,sm %b[42], %b[56], %b[54], %b[12] qpshufb,4,sm %b[46], %b[46], %r10, %b[32] qpfsubs,5,sm %b[48], %b[45], %b[16] } { loop_mode qppermb,1,sm %b[26], %b[27], %r0, %b[34] qpshufb,3,sm %b[39], %b[39], %r10, %b[48] qppermb,4,sm %b[53], %b[57], %r0, %b[45] staaqp,5 %b[37], %aad4[ %aasti8 ] incr,5 %aaincr0 movaqp,1 area=1, ind=0, am=1, be=0, %b[42] movaqp,3 area=1, ind=0, am=1, be=0, %b[11] } { loop_mode qpfmul_hsubs,0,sm %b[41], %b[33], %r14, %b[27] qppermb,1,sm %b[23], %b[6], %r9, %b[54] qpfmul_hadds,2,sm %b[50], %b[33], %r14, %b[26] qpshufb,3,sm %b[52], %b[59], %r11, %b[53] qpshufb,4,sm %b[43], %b[58], %r11, %b[49] staaqp,5 %b[22], %aad2[ %aasti6 ] incr,5 %aaincr0 movaqp,1 area=2, ind=0, am=1, be=0, %b[37] } { loop_mode qpfmul_hsubs,0,sm %b[46], %b[9], %r14, %b[6] qpshufb,1,sm %b[18], %b[18], %r10, %b[60] qpfadd_adds,2,sm %b[40], %b[54], %b[2], %b[33] qpshufb,3,sm %b[52], %b[59], %r13, %b[23] qpshufb,4,sm %b[43], %b[58], %r13, %b[22] staaqp,5 %b[3], %aad3[ %aasti7 ] incr,5 %aaincr0 movaqp,0 area=0, ind=0, am=0, be=0, %b[57] movaqp,1 area=0, ind=16, am=1, be=0, %b[56] movaqp,2 area=0, ind=0, am=0, be=0, %b[50] movaqp,3 area=0, ind=16, am=1, be=0, %b[41] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qpfmul_hadds,0,sm %b[32], %b[9], %r14, %b[3] qpxor,1,sm %b[60], %r7, %b[52] qpfadd_rsubs,2,sm %b[40], %b[54], %b[2], %b[18] qppermb,3,sm %b[30], %b[31], %r9, %b[43] qppermb,4,sm %b[7], %b[10], %r9, %b[46] staaqp,5 %b[14], %aad1[ %aasti5 ] incr,5 %aaincr0 }
Теоретическая скорость: 8 комплексных чисел за 6 тактов (8/6) = 10.67 Байт/такт
Двойная теоретическая скорость: 21.33 Байт/такт
Замеры скорости

4. lastStage_radix4_simd128_noConj_unroll2
Здесь происходит раскрутка цикла в 2 раза с помощью директивы unroll.
Код на Си
void lastStage_radix4_simd128_noConj_unroll2(int data_count, myComplex4 *data_in, myComplex *data_out, myComplex *coefC, myComplex *coefD, myComplex *coefE) { __v2di *xyzw0_real_in = (__v2di*)&data_in[0].real; __v2di *xyzw0_imag_in = (__v2di*)&data_in[0].imag; __v2di *xyzw1_real_in = (__v2di*)&data_in[1].real; __v2di *xyzw1_imag_in = (__v2di*)&data_in[1].imag; __v2di *c_in = (__v2di*)coefC; __v2di *d_in = (__v2di*)coefD; __v2di *e_in = (__v2di*)coefE; __v2di *out_0 = (__v2di*)&data_out[0*data_count/4]; __v2di *out_1 = (__v2di*)&data_out[1*data_count/4]; __v2di *out_2 = (__v2di*)&data_out[2*data_count/4]; __v2di *out_3 = (__v2di*)&data_out[3*data_count/4]; #pragma ivdep #pragma unroll(2) #pragma prefetch for(int64_t i = 0; i < data_count/8; ++i) { __v2di xyzw0_real = xyzw0_real_in[4*i]; __v2di xyzw0_imag = xyzw0_imag_in[4*i]; __v2di xyzw1_real = xyzw1_real_in[4*i]; __v2di xyzw1_imag = xyzw1_imag_in[4*i]; __v2di c = c_in[i]; __v2di d = d_in[i]; __v2di e = e_in[i]; __v2di xy_real = __builtin_e2k_qpshufb(xyzw1_real, xyzw0_real, (__v2di){0x0706050403020100, 0x0706050403020100}); __v2di zw_real = __builtin_e2k_qpshufb(xyzw1_real, xyzw0_real, (__v2di){0x0F0E0D0C0B0A0908, 0x0F0E0D0C0B0A0908}); __v2di xy_imag = __builtin_e2k_qpshufb(xyzw1_imag, xyzw0_imag, (__v2di){0x0706050403020100, 0x0706050403020100}); __v2di zw_imag = __builtin_e2k_qpshufb(xyzw1_imag, xyzw0_imag, (__v2di){0x0F0E0D0C0B0A0908, 0x0F0E0D0C0B0A0908}); __v2di x = __builtin_e2k_qppermb(xy_imag, xy_real, (__v2di){0x1312111003020100, 0x1B1A19180B0A0908}); __v2di y = __builtin_e2k_qppermb(xy_imag, xy_real, (__v2di){0x1716151407060504, 0x1F1E1D1C0F0E0D0C}); __v2di z = __builtin_e2k_qppermb(zw_imag, zw_real, (__v2di){0x1312111003020100, 0x1B1A19180B0A0908}); __v2di w = __builtin_e2k_qppermb(zw_imag, zw_real, (__v2di){0x1716151407060504, 0x1F1E1D1C0F0E0D0C}); __v2di swap_c = __builtin_e2k_qpshufb(c, c, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di swap_d = __builtin_e2k_qpshufb(d, d, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di swap_e = __builtin_e2k_qpshufb(e, e, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di cy_real = __builtin_e2k_qpfmuls( c, y); __v2di dz_real = __builtin_e2k_qpfmuls( d, z); __v2di ew_real = __builtin_e2k_qpfmuls( e, w); __v2di cy_imag = __builtin_e2k_qpfmuls(swap_c, y); __v2di dz_imag = __builtin_e2k_qpfmuls(swap_d, z); __v2di ew_imag = __builtin_e2k_qpfmuls(swap_e, w); __v2di cy_rr = __builtin_e2k_qpfhsubs((__v2di){0}, cy_real); __v2di dz_rr = __builtin_e2k_qpfhsubs((__v2di){0}, dz_real); __v2di ew_rr = __builtin_e2k_qpfhsubs((__v2di){0}, ew_real); __v2di cy_ii = __builtin_e2k_qpfhadds((__v2di){0}, cy_imag); __v2di dz_ii = __builtin_e2k_qpfhadds((__v2di){0}, dz_imag); __v2di ew_ii = __builtin_e2k_qpfhadds((__v2di){0}, ew_imag); __v2di cy = __builtin_e2k_qppermb(cy_ii, cy_rr, (__v2di){0x1B1A19180B0A0908, 0x1F1E1D1C0F0E0D0C}); __v2di dz = __builtin_e2k_qppermb(dz_ii, dz_rr, (__v2di){0x1B1A19180B0A0908, 0x1F1E1D1C0F0E0D0C}); __v2di ew = __builtin_e2k_qppermb(ew_ii, ew_rr, (__v2di){0x1B1A19180B0A0908, 0x1F1E1D1C0F0E0D0C}); __v2di add02 = __builtin_e2k_qpfadds( x, dz); __v2di sub02 = __builtin_e2k_qpfsubs( x, dz); __v2di add13 = __builtin_e2k_qpfadds(cy, ew); __v2di sub13 = __builtin_e2k_qpfsubs(cy, ew); //__v2di conj_sub13 = __builtin_e2k_qpxor(sub13, (__v2di){1LL<<63, 1LL<<63}); //__v2di sub13i = __builtin_e2k_qpshufb(conj_sub13, conj_sub13, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di swap_sub13 = __builtin_e2k_qpshufb(sub13, sub13, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di sub13i = __builtin_e2k_qpxor(swap_sub13, (__v2di){1LL<<31, 1LL<<31}); out_0[i] = __builtin_e2k_qpfadds(add02, add13); out_1[i] = __builtin_e2k_qpfsubs(sub02, sub13i); out_2[i] = __builtin_e2k_qpfsubs(add02, add13); out_3[i] = __builtin_e2k_qpfadds(sub02, sub13i); } }
Основной цикл на ассемблере
.L3893: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=3, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=3, abs=0, disp=32 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=3, abs=8, disp=64 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=3, abs=8, disp=96 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=4, asz=3, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=3, asz=4, abs=16, disp=0 } { fapb ct=1, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=2, asz=3, abs=24, disp=0 } .L3037: { loop_mode qpfsub_adds,0,sm %b[16], %b[71], %b[66], %b[57] qpshufb,1,sm %b[32], %b[32], %r14, %b[71] qpfsub_rsubs,2,sm %b[16], %b[71], %b[66], %b[64] qppermb,3,sm %b[38], %b[42], %r11, %b[0] qppermb,4,sm %b[73], %b[76], %r11, %b[1] qpfadds,5,sm %b[64], %b[57], %b[66] } { loop_mode qpfmul_hsubs,0,sm %b[32], %b[83], %r15, %b[74] qpshufb,1,sm %b[10], %b[10], %r14, %b[73] qpfmul_hadds,2,sm %b[71], %b[83], %r15, %b[71] qpshufb,4,sm %b[21], %b[25], %r9, %b[32] qpfadds,5,sm %b[1], %b[0], %b[16] } { loop_mode qpfmul_hsubs,0,sm %b[31], %b[75], %r15, %b[38] qppermb,1,sm %b[69], %b[34], %r13, %b[31] qpfmul_hadds,2,sm %b[72], %b[75], %r15, %b[34] qpshufb,3,sm %b[19], %b[19], %r14, %b[54] qppermb,4,sm %b[54], %b[55], %r13, %b[42] } { loop_mode qpfmul_hsubs,0,sm %b[10], %b[65], %r15, %g17 qpshufb,1,sm %b[26], %b[26], %r14, %b[65] qpfmul_hadds,2,sm %b[73], %b[65], %r15, %g16 qppermb,3,sm %g16, %g17, %r11, %b[55] qppermb,4,sm %b[62], %b[63], %r11, %b[62] qpfsubs,5,sm %b[3], %b[2], %b[63] } { loop_mode qpfmul_hsubs,0,sm %b[41], %b[82], %r15, %b[61] qppermb,1,sm %b[80], %b[78], %r11, %b[72] qpfmul_hadds,2,sm %b[61], %b[82], %r15, %b[60] qpshufb,3,sm %b[43], %b[46], %r9, %b[67] qppermb,4,sm %b[60], %b[67], %r11, %b[69] qpfsubs,5,sm %b[62], %b[55], %b[2] } { loop_mode qpfmul_hsubs,0,sm %b[26], %b[35], %r15, %b[65] qppermb,1,sm %b[58], %b[70], %r13, %b[3] qpfmul_hadds,2,sm %b[65], %b[35], %r15, %b[58] qpshufb,3,sm %b[29], %b[29], %r14, %b[70] qppermb,4,sm %b[67], %b[32], %r12, %b[73] staaqp,5 %b[77], %aad4[ %aasti8 ] } { loop_mode qpfmul_hsubs,0,sm %b[19], %b[44], %r15, %b[76] qppermb,1,sm %b[56], %b[68], %r13, %b[10] qpfadd_adds,2,sm %b[7], %b[72], %b[66], %b[75] qpshufb,3,sm %b[49], %b[53], %r0, %b[68] qpshufb,4,sm %b[11], %b[52], %r0, %b[56] staaqp,5 %b[79], %aad2[ %aasti6 ] } { loop_mode qpfmul_hadds,0,sm %b[54], %b[44], %r15, %b[78] qpshufb,1,sm %b[4], %b[4], %r14, %b[79] qpfadd_rsubs,2,sm %b[7], %b[72], %b[66], %b[77] qpshufb,3,sm %b[23], %b[27], %r0, %b[66] qpshufb,4,sm %b[45], %b[48], %r0, %b[54] staaqp,5 %b[81], %aad2[ %aasti6 + _f32s,_lts0 0x10 ] incr,5 %aaincr3 movaqp,0 area=3, ind=0, am=0, be=0, %b[4] movaqp,1 area=1, ind=0, am=0, be=0, %b[23] movaqp,3 area=1, ind=0, am=0, be=0, %b[19] } { loop_mode qpfadd_adds,0,sm %b[14], %b[69], %b[18], %g18 qpxor,1,sm %b[79], %r10, %b[82] qpfadd_rsubs,2,sm %b[14], %b[69], %b[18], %b[79] qppermb,3,sm %b[56], %b[68], %r12, %b[80] qppermb,4,sm %b[54], %b[66], %r12, %b[81] staaqp,5 %g18, %aad4[ %aasti8 + _f32s,_lts0 0x10 ] incr,5 %aaincr3 movaqp,0 area=3, ind=16, am=1, be=0, %b[27] movaqp,1 area=2, ind=16, am=0, be=0, %b[26] movaqp,3 area=2, ind=16, am=0, be=0, %b[18] } { loop_mode qpfsub_adds,0,sm %b[7], %b[72], %b[82], %g19 qpshufb,1,sm %b[63], %b[63], %r14, %b[59] staaqp,2 %g19, %aad1[ %aasti5 ] qpshufb,3,sm %b[49], %b[53], %r9, %b[53] qpshufb,4,sm %b[11], %b[52], %r9, %b[52] staaqp,5 %b[59], %aad3[ %aasti7 ] movaqp,0 area=2, ind=0, am=1, be=0, %b[35] movaqp,1 area=1, ind=16, am=1, be=0, %b[44] movaqp,2 area=2, ind=0, am=1, be=0, %b[11] movaqp,3 area=1, ind=16, am=1, be=0, %b[41] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qpfsub_rsubs,0,sm %b[7], %b[72], %b[82], %b[57] qpxor,1,sm %b[59], %r10, %b[64] staaqp,2 %b[57], %aad1[ %aasti5 + _f32s,_lts0 0x10 ] incr,2 %aaincr3 qpshufb,3,sm %b[39], %b[39], %r14, %b[59] qppermb,4,sm %b[52], %b[53], %r12, %b[63] staaqp,5 %b[64], %aad3[ %aasti7 + _f32s,_lts0 0x10 ] incr,5 %aaincr3 movaqp,0 area=0, ind=0, am=0, be=0, %b[49] movaqp,1 area=0, ind=16, am=1, be=0, %b[48] movaqp,2 area=0, ind=0, am=0, be=0, %b[45] movaqp,3 area=0, ind=16, am=1, be=0, %b[7] }
Теоретическая скорость: 16 комплексных чисел за 11 тактов (16/11) = 11.64 Байт/такт
Двойная теоретическая скорость: 23.27 Байт/такт
Замеры скорости

5. lastStage_radix4_simd128_noConj_unroll3
Здесь происходит раскрутка цикла в 3 раза с помощью директивы unroll.
Код на Си
void lastStage_radix4_simd128_noConj_unroll3(int data_count, myComplex4 *data_in, myComplex *data_out, myComplex *coefC, myComplex *coefD, myComplex *coefE) { __v2di *xyzw0_real_in = (__v2di*)&data_in[0].real; __v2di *xyzw0_imag_in = (__v2di*)&data_in[0].imag; __v2di *xyzw1_real_in = (__v2di*)&data_in[1].real; __v2di *xyzw1_imag_in = (__v2di*)&data_in[1].imag; __v2di *c_in = (__v2di*)coefC; __v2di *d_in = (__v2di*)coefD; __v2di *e_in = (__v2di*)coefE; __v2di *out_0 = (__v2di*)&data_out[0*data_count/4]; __v2di *out_1 = (__v2di*)&data_out[1*data_count/4]; __v2di *out_2 = (__v2di*)&data_out[2*data_count/4]; __v2di *out_3 = (__v2di*)&data_out[3*data_count/4]; #pragma ivdep #pragma unroll(3) #pragma prefetch for(int64_t i = 0; i < data_count/8; ++i) { __v2di xyzw0_real = xyzw0_real_in[4*i]; __v2di xyzw0_imag = xyzw0_imag_in[4*i]; __v2di xyzw1_real = xyzw1_real_in[4*i]; __v2di xyzw1_imag = xyzw1_imag_in[4*i]; __v2di c = c_in[i]; __v2di d = d_in[i]; __v2di e = e_in[i]; __v2di xy_real = __builtin_e2k_qpshufb(xyzw1_real, xyzw0_real, (__v2di){0x0706050403020100, 0x0706050403020100}); __v2di zw_real = __builtin_e2k_qpshufb(xyzw1_real, xyzw0_real, (__v2di){0x0F0E0D0C0B0A0908, 0x0F0E0D0C0B0A0908}); __v2di xy_imag = __builtin_e2k_qpshufb(xyzw1_imag, xyzw0_imag, (__v2di){0x0706050403020100, 0x0706050403020100}); __v2di zw_imag = __builtin_e2k_qpshufb(xyzw1_imag, xyzw0_imag, (__v2di){0x0F0E0D0C0B0A0908, 0x0F0E0D0C0B0A0908}); __v2di x = __builtin_e2k_qppermb(xy_imag, xy_real, (__v2di){0x1312111003020100, 0x1B1A19180B0A0908}); __v2di y = __builtin_e2k_qppermb(xy_imag, xy_real, (__v2di){0x1716151407060504, 0x1F1E1D1C0F0E0D0C}); __v2di z = __builtin_e2k_qppermb(zw_imag, zw_real, (__v2di){0x1312111003020100, 0x1B1A19180B0A0908}); __v2di w = __builtin_e2k_qppermb(zw_imag, zw_real, (__v2di){0x1716151407060504, 0x1F1E1D1C0F0E0D0C}); __v2di swap_c = __builtin_e2k_qpshufb(c, c, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di swap_d = __builtin_e2k_qpshufb(d, d, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di swap_e = __builtin_e2k_qpshufb(e, e, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di cy_real = __builtin_e2k_qpfmuls( c, y); __v2di dz_real = __builtin_e2k_qpfmuls( d, z); __v2di ew_real = __builtin_e2k_qpfmuls( e, w); __v2di cy_imag = __builtin_e2k_qpfmuls(swap_c, y); __v2di dz_imag = __builtin_e2k_qpfmuls(swap_d, z); __v2di ew_imag = __builtin_e2k_qpfmuls(swap_e, w); __v2di cy_rr = __builtin_e2k_qpfhsubs((__v2di){0}, cy_real); __v2di dz_rr = __builtin_e2k_qpfhsubs((__v2di){0}, dz_real); __v2di ew_rr = __builtin_e2k_qpfhsubs((__v2di){0}, ew_real); __v2di cy_ii = __builtin_e2k_qpfhadds((__v2di){0}, cy_imag); __v2di dz_ii = __builtin_e2k_qpfhadds((__v2di){0}, dz_imag); __v2di ew_ii = __builtin_e2k_qpfhadds((__v2di){0}, ew_imag); __v2di cy = __builtin_e2k_qppermb(cy_ii, cy_rr, (__v2di){0x1B1A19180B0A0908, 0x1F1E1D1C0F0E0D0C}); __v2di dz = __builtin_e2k_qppermb(dz_ii, dz_rr, (__v2di){0x1B1A19180B0A0908, 0x1F1E1D1C0F0E0D0C}); __v2di ew = __builtin_e2k_qppermb(ew_ii, ew_rr, (__v2di){0x1B1A19180B0A0908, 0x1F1E1D1C0F0E0D0C}); __v2di add02 = __builtin_e2k_qpfadds( x, dz); __v2di sub02 = __builtin_e2k_qpfsubs( x, dz); __v2di add13 = __builtin_e2k_qpfadds(cy, ew); __v2di sub13 = __builtin_e2k_qpfsubs(cy, ew); //__v2di conj_sub13 = __builtin_e2k_qpxor(sub13, (__v2di){1LL<<63, 1LL<<63}); //__v2di sub13i = __builtin_e2k_qpshufb(conj_sub13, conj_sub13, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di swap_sub13 = __builtin_e2k_qpshufb(sub13, sub13, (__v2di){0x0302010007060504, 0x0B0A09080F0E0D0C}); __v2di sub13i = __builtin_e2k_qpxor(swap_sub13, (__v2di){1LL<<31, 1LL<<31}); out_0[i] = __builtin_e2k_qpfadds(add02, add13); out_1[i] = __builtin_e2k_qpfsubs(sub02, sub13i); out_2[i] = __builtin_e2k_qpfsubs(add02, add13); out_3[i] = __builtin_e2k_qpfadds(sub02, sub13i); } }
Основной цикл на ассемблере
.L5077: { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=0, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=0, disp=32 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=4, disp=64 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=4, disp=96 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=8, disp=128 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=1, ind=1, asz=2, abs=8, disp=160 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=4, asz=2, abs=12, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=3, asz=2, abs=12, disp=0 } { fapb ct=0, dcd=0, fmt=5, mrng=0, d=0, incr=2, ind=2, asz=3, abs=16, disp=0 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=2, ind=4, asz=3, abs=16, disp=32 } { fapb ct=1, dcd=0, fmt=5, mrng=16, d=0, incr=2, ind=3, asz=3, abs=24, disp=32 fapb dpl=0, dcd=0, fmt=5, mrng=16, d=0, incr=2, ind=2, asz=3, abs=24, disp=32 } .L3947: { loop_mode qpfmul_hadds,0,sm %b[93], %b[76], %r15, %b[75] qpshufb,1,sm %b[85], %b[85], %r13, %b[91] qpfadd_adds,2,sm %b[32], %b[87], %b[75], %b[76] qppermb,3,sm %b[18], %b[17], %r10, %b[86] qppermb,4,sm %b[91], %b[86], %r10, %b[85] qpfsubs,5,sm %b[88], %b[89], %b[0] } { loop_mode qpfmul_hsubs,0,sm %b[52], %b[94], %r15, %b[93] qpxor,1,sm %b[91], %r12, %b[105] qpfadd_adds,2,sm %b[80], %b[77], %b[83], %b[91] qppermb,3,sm %b[44], %b[69], %r11, %b[103] qppermb,4,sm %b[22], %b[27], %r11, %b[104] qpfsubs,5,sm %b[85], %b[86], %b[83] } { loop_mode qpfmul_hadds,0,sm %b[97], %b[94], %r15, %b[85] qpxor,1,sm %b[102], %r12, %b[97] qpfsub_adds,2,sm %b[80], %b[77], %b[105], %b[82] qppermb,3,sm %b[101], %b[100], %r10, %b[94] qppermb,4,sm %b[82], %b[81], %r10, %b[86] qpfadds,5,sm %b[85], %b[86], %b[81] } { loop_mode qpfmul_hadds,0,sm %b[95], %b[72], %r15, %b[100] qpshufb,1,sm %b[2], %b[2], %r13, %b[101] qpfsub_adds,2,sm %b[73], %b[78], %b[97], %b[95] qpshufb,3,sm %b[39], %b[65], %r0, %b[1] qpshufb,4,sm %b[68], %b[60], %r0, %b[2] qpfsubs,5,sm %b[86], %b[94], %b[72] movaqp,1 area=4, ind=16, am=0, be=0, %b[8] movaqp,3 area=5, ind=0, am=1, be=0, %b[7] } { loop_mode qpfsub_rsubs,0,sm %b[80], %b[77], %b[105], %b[78] qpxor,1,sm %b[101], %r12, %b[88] qpfsub_rsubs,2,sm %b[73], %b[78], %b[97], %b[77] qpshufb,3,sm %b[99], %b[98], %r0, %b[18] qppermb,4,sm %b[2], %b[1], %r11, %b[80] qpfadds,5,sm %b[88], %b[89], %b[73] movaqp,0 area=5, ind=0, am=1, be=0, %b[17] movaqp,1 area=1, ind=0, am=0, be=0, %b[23] movaqp,3 area=1, ind=0, am=0, be=0, %b[26] } { loop_mode qpfsub_adds,0,sm %b[32], %b[87], %b[88], %b[87] qpshufb,1,sm %b[61], %b[61], %r13, %b[89] qpfsub_rsubs,2,sm %b[32], %b[87], %b[88], %b[86] qpshufb,3,sm %b[99], %b[98], %r9, %b[39] qpshufb,4,sm %b[96], %b[92], %r9, %b[45] qpfadds,5,sm %b[86], %b[94], %b[88] movaqp,0 area=4, ind=0, am=1, be=0, %b[31] movaqp,1 area=1, ind=16, am=1, be=0, %b[32] movaqp,2 area=4, ind=0, am=1, be=0, %b[40] movaqp,3 area=1, ind=16, am=1, be=0, %b[36] } { loop_mode qpfmul_hsubs,0,sm %b[61], %b[104], %r15, %b[84] qpshufb,1,sm %b[35], %b[35], %r13, %b[94] qpfmul_hadds,2,sm %b[89], %b[104], %r15, %b[89] qpshufb,3,sm %b[96], %b[92], %r0, %b[60] qppermb,4,sm %b[45], %b[39], %r11, %b[92] staaqp,5 %b[84], %aad4[ %aasti8 ] movaqp,0 area=3, ind=0, am=0, be=0, %b[52] movaqp,1 area=3, ind=16, am=1, be=0, %b[57] movaqp,2 area=3, ind=0, am=0, be=0, %b[46] movaqp,3 area=3, ind=16, am=1, be=0, %b[51] } { loop_mode qpfmul_hsubs,0,sm %b[35], %b[103], %r15, %b[98] qpshufb,1,sm %b[56], %b[56], %r13, %b[103] qpfmul_hadds,2,sm %b[94], %b[103], %r15, %b[99] qpshufb,3,sm %b[42], %b[42], %r13, %b[101] qppermb,4,sm %b[60], %b[18], %r11, %b[102] staaqp,5 %b[90], %aad2[ %aasti6 ] movaqp,0 area=2, ind=16, am=1, be=0, %b[90] movaqp,1 area=2, ind=0, am=0, be=0, %b[96] movaqp,2 area=2, ind=16, am=1, be=0, %b[94] movaqp,3 area=2, ind=0, am=0, be=0, %b[97] } { loop_mode qpfmul_hsubs,0,sm %b[56], %b[80], %r15, %b[79] qppermb,1,sm %b[11], %b[12], %r11, %b[103] qpfmul_hadds,2,sm %b[103], %b[80], %r15, %b[80] qpshufb,3,sm %b[7], %b[7], %r13, %b[65] qpshufb,4,sm %b[8], %b[8], %r13, %b[68] staaqp,5 %b[79], %aad2[ %aasti6 + _f32s,_lts0 0x20 ] movaqp,0 area=0, ind=16, am=1, be=0, %b[56] movaqp,1 area=0, ind=0, am=0, be=0, %b[61] movaqp,2 area=0, ind=16, am=1, be=0, %b[64] movaqp,3 area=0, ind=0, am=0, be=0, %b[35] } { loop_mode qpfmul_hsubs,0,sm %b[10], %b[103], %r15, %b[13] qppermb,1,sm %b[47], %b[41], %r14, %b[70] qpfmul_hadds,2,sm %b[70], %b[103], %r15, %b[14] qpshufb,3,sm %b[26], %b[23], %r9, %b[10] qppermb,4,sm %b[13], %b[14], %r14, %b[74] staaqp,5 %b[74], %aad2[ %aasti6 + _f32s,_lts0 0x10 ] incr,5 %aaincr3 } { loop_mode qpfmul_hsubs,0,sm %b[9], %b[92], %r15, %b[41] qppermb,1,sm %b[85], %b[93], %r10, %b[76] qpfmul_hadds,2,sm %b[67], %b[92], %r15, %b[47] qpshufb,3,sm %b[36], %b[32], %r9, %b[9] qpshufb,4,sm %b[37], %b[63], %r9, %b[67] staaqp,5 %b[76], %aad4[ %aasti8 + _f32s,_lts0 0x20 ] } { loop_mode qpfmul_hsubs,0,sm %b[42], %b[102], %r15, %b[3] qppermb,1,sm %b[4], %b[3], %r14, %b[71] qpfmul_hadds,2,sm %b[101], %b[102], %r15, %b[4] qpshufb,3,sm %b[66], %b[58], %r9, %b[42] qppermb,4,sm %b[75], %b[71], %r10, %b[75] staaqp,5 %b[91], %aad4[ %aasti8 + _f32s,_lts0 0x10 ] incr,5 %aaincr3 } { loop_mode qpfmul_hsubs,0,sm %b[21], %b[70], %r15, %g16 qppermb,1,sm %b[100], %g16, %r10, %b[85] qpfadd_adds,2,sm %b[71], %b[76], %b[88], %b[82] qpshufb,3,sm %b[28], %b[25], %r0, %b[25] qpshufb,4,sm %b[55], %b[55], %r13, %b[91] staaqp,5 %b[82], %aad1[ %aasti5 + _f32s,_lts0 0x10 ] } { loop_mode qpfmul_hsubs,0,sm %b[55], %b[74], %r15, %b[69] qppermb,1,sm %b[62], %b[20], %r14, %b[28] qpfadd_rsubs,2,sm %b[71], %b[76], %b[88], %b[88] qpshufb,3,sm %b[38], %b[34], %r0, %b[20] qppermb,4,sm %b[44], %b[69], %r14, %b[92] staaqp,5 %b[95], %aad1[ %aasti5 ] } { loop_mode qpfadd_rsubs,0,sm %b[30], %b[85], %b[73], %b[77] qppermb,1,sm %b[24], %b[29], %r14, %b[78] staaqp,2 %b[78], %aad3[ %aasti7 + _f32s,_lts0 0x10 ] qpshufb,3,sm %b[21], %b[21], %r13, %b[93] qpshufb,4,sm %b[50], %b[50], %r13, %b[95] staaqp,5 %b[77], %aad3[ %aasti7 ] } { loop_mode alc alcf=1, alct=1 abn abnf=1, abnt=1 ct %ctpr1 ? %NOT_LOOP_END qpfadd_rsubs,0,sm %b[78], %b[75], %b[81], %b[72] qpshufb,1,sm %b[72], %b[72], %r13, %b[100] staaqp,2 %b[87], %aad1[ %aasti5 + _f32s,_lts0 0x20 ] incr,2 %aaincr3 qppermb,3,sm %b[49], %b[43], %r10, %b[87] qppermb,4,sm %b[6], %b[5], %r10, %b[86] staaqp,5 %b[86], %aad3[ %aasti7 + _f32s,_lts0 0x20 ] incr,5 %aaincr3 }
Теоретическая скорость: 24 комплексных числа за 16 тактов (24/16) = 12 Байт/такт
Двойная теоретическая скорость: 24 Байт/такт
Замеры скорости

Итоги по lastStage_radix4


При реализации Vector-4 FFT будем использовать lastStage_radix4_simd128_noConj_unroll2.
Собираем FFT
fft_radix2_vector2
Собираем fft_radix2_vector2 из reverse_radix2_vector2_stream16, всех вариантов stage_radix2_vector2 и lastStage_radix2_simd128_noConj.


fft_radix2_vector4
Собираем fft_radix2_vector4 из reverse_radix2_vector4_stream8, всех вариантов stage_radix2_vector4 и lastStage_radix4_simd128_noConj_unroll2.


fft_radix4_vector2
Собираем fft_radix4_vector2 из reverse_radix4_vector2_stream16, всех вариантов stage_radix4_vector2 и lastStage_radix2_simd128_noConj.


fft_radix4_vector4
Собираем fft_radix4_vector4 из reverse_radix4_vector4_stream16, всех вариантов stage_radix4_vector4 и lastStage_radix4_simd128_noConj_unroll2.


Дальнейшие улучшения имеющегося кода
Что изменить в текущем коде:
Перемежить коэффициенты.
Сейчас коэффициенты c/d/e размещаются в нескольких массивах.
Нужно разместить их в одном массиве, перемежив между собой.
Это повысит эффективность чтения памяти.
Скорость вне кэша должна вырасти.Заменить типы на векторные для упрощения внешнего вида кода.
Сейчас везде используются интринсики вместе с типамиuint64_tи__v2di.
Нужно заменитьuint64_tи__v2diна__v2sfи__v4sfсоответственно.
Тогда можно будет заменить интринсики на операции «+», «-», «*».
Код станет выглядеть менее громоздко.
Дальнейшие направления создания кода
Что делать дальше (в дополнение к указанному в предыдущей статье):
Рассмотреть все Stage в качестве LastStage.
Сейчас в качестве LastStage рассмотрены только некоторые Stage из прошлой статьи. Для полноты картины нужно рассмотреть остальные варианты Stage. Небольшое ускорение внутри кэша и замедление вне кэша ожидается от вариантов"_readConjSwap".Оптимизация для малых размеров входных данных (как в EML).
На графиках скорости FFT можно заметить выделяющийся угол у версии из EML.
Скорее всего, в EML сделана оптимизация для малых длин массивов (до 64 элементов). Оптимизация может состоять в хранении промежуточных результатов вычислений (между последовательными Stage) прямо в регистрах, а не в памяти. Если размеры входных данных достаточно малы, то такие данные можно целиком разместить в регистрах. В этом случае этап Reverse не нужен. Вместо него нужно просто переименовать регистры в первом Stage.
Заключение
Прямая векторизация проще для написания, понятнее для чтения и работает быстрее, чем тот код, что был написан в прошлой статье. Однако, нельзя сказать, что та работа была сделана напрасно. Ведь LastStage написан на основе кода из прошлой статьи.
Судя по графикам, версия из EML аналогична fft_radix2_vector4 с добавлением перемежения коэффициентов и оптимизации для малых размеров входных данных.
Пока я писал этот код, возникло ощущение, что было бы удобно создать классы myComplex, myComplex2 и myComplex4. Но сейчас думаю, что если заменить myComplex на родной float complex, то классы уже и не нужны.
