Данная статья является продолжением предыдущей статьи.
Здесь рассматривается вариант прямой векторизации алгоритма 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
Пишем векторный Stage
Пишем LastStage
Собираем FFT

Что такое прямая векторизация

Для ряда операций (сложение, умножение и так далее) в процессоре Эльбрус присутствуют векторные инструкции (SIMD). Векторные инструкции выполняют несколько одинаковых операций одновременно.

В предыдущей статье векторные инструкции использовались просто как набор из нескольких инструкций в одной. Например, вместо двух инструкций умножения float чисел, нужных по алгоритму, применялась одна векторная, выполняющая два умножения (SIMD64).
Проблема такого подхода в том, что нет простого способа ускорить алгоритм через замену инструкции SIMD64 на аналогичную SIMD128. Использование SIMD128 приводило к появлению инструкций перетасовки данных (shuf, perm), занимавших исполнительные юниты.

В этой статье векторные инструкции используются по‑другому.
Здесь с их помощью создаются «векторные» версии алгоритмов из исходных.
Векторные алгоритмы выполняют несколько одинаковых задач одновременно аналогично тому, как векторные инструкции выполняют несколько одинаковых операций одновременно.
Такой подход я и называю «прямой векторизацией».

Для выполнения прямой векторизации нужно заменить обычные инструкции на их векторные версии. Чтобы такое было возможно, в исходной программе должны использоваться инструкции, для которых существуют векторные версии. Обрабатываемые данные также требуется привести в векторную форму, то есть такой формат, который ожидается векторными инструкциями.

Прямая векторизация кода на Си

Пусть, например, алгоритм обрабатывает float числа.
Для выполнения прямой векторизации в 2 или 4 раза нужно заменить в Си‑коде исходного алгоритма тип float на __v2sf или __v4sf соответственно. И компилятор сгенерирует векторизованный код с использованием инструкций SIMD64 или SIMD128 соответственно.
При этом операции «+», «-», «*» будут скомпилированы в векторные версии инструкций.

Обрабатываемые данные также требуется предварительно привести в векторную форму:

  • там, где было одно float число, теперь должен быть «вектор» float чисел (расположенные рядом 2 или 4 float числа)

  • там, где был массив 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 от одного набора комплексных чисел?

Для этого нужно вспомнить, как выглядел рекурсивный алгоритм Кули‑Тьюки:

  1. Вычисляются FFT от каждого из перемежённых массивов комплексных чисел.

  2. Вычисляется итоговый 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 из прошлой статьи).


Итого, полный порядок действий выглядит так:

  1. Исходные данные преобразуются в векторную форму.

  2. Выполняется векторный Reverse.

  3. Несколько раз выполняется векторный Stage.

  4. Полученные данные преобразуются из векторной формы в исходную.

  5. Выполняется 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 Байт/такт

Замеры скорости