Данная статья является продолжением предыдущей статьи.
Здесь рассматривается вариант прямой векторизации алгоритма FFT.
Перед чтением рекомендуется бегло ознакомиться с предыдущей статьёй.
Промежуточные результаты представлены на графиках в конце каждого раздела реализации.
Итоговые результаты показаны на графиках в разделе Собираем FFT
Исходный код
Исходный код лежит здесь.
Структура кода намеренно сделана как можно проще (в смысле «simple», а не «easy»).
Приоритеты в порядке убывания:
— Скорость выполнения
— Единообразие
— Понятность
Компиляция:
./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 написано здесь.
# Версия lcc: $ lcc --version lcc:1.29.16:Jan-22-2026:e2k-2c3-linux gcc (GCC) 11.3.0 compatible # Версия ОС $ uname -a Linux BB8 6.1.0-1.11-e2c3 #1 SMP Mon Mar 30 14:31:34 UTC 2026 e2k E2C3 Elbrus-MCST GNU/Linux
Как измерялось время
Замеры времени делались с помощью функции 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
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 компилятор вставляет инструкции предподкачки данных в кэш L1 (в коде они выглядят как обычные инструкции чтения в регистр с названием «empty»), обычные инструкции чтения этих данных (почему‑то не сразу ldqp, а набором ldd + ldd + qppackdl) и инструкции инкремента индексов для всех этих чтений.
В итоге скорость резко проседает.
Итоги по 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().
Сразу напишем код так, чтобы вместиться в один такт (убираем инструкции shrd и shld).
Код на Си
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 компилятор вставляет инструкции предподкачки данных в кэш L1 (в коде они выглядят как обычные инструкции чтения в регистр с названием «empty»), обычные инструкции чтения этих данных (почему‑то не сразу ldqp, а набором ldd + ldd + qppackdl) и инструкции инкремента индексов для всех этих чтений.
В итоге скорость резко проседает.
Итоги по 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 компилятор вставляет инструкции предподкачки данных в кэш L1 (в коде они выглядят как обычные инструкции чтения в регистр с названием «empty»), обычные инструкции чтения этих данных (почему‑то не сразу ldqp, а набором ldd + ldd + qppackdl) и инструкции инкремента индексов для всех этих чтений.
В итоге скорость резко проседает.
Но можно сделать чтение в 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 Байт/такт