умножение неоновых чисел происходит медленнее, чем ожидалось

У меня есть две вкладки поплавков. Мне нужно умножить элементы из первой вкладки на соответствующие элементы из второй вкладки и сохранить результат в третьей вкладке.

Я хотел бы использовать NEON для распараллеливания умножений с плавающей точкой: четыре умножения с плавающей точкой одновременно вместо одной.

Я ожидал значительного ускорения, но достиг всего лишь 20% сокращения времени выполнения. Это мой код:

#include <stdlib.h>
#include <iostream>
#include <arm_neon.h>

const int n = 100; // table size

/* fill a tab with random floats */
void rand_tab(float *t) {
for (int i = 0; i < n; i++)
t[i] = (float)rand()/(float)RAND_MAX;
}

/* Multiply elements of two tabs and store results in third tab
- STANDARD processing. */
void mul_tab_standard(float *t1, float *t2, float *tr) {
for (int i = 0; i < n; i++)
tr[i] = t1[i] * t2[i];
}

/* Multiply elements of two tabs and store results in third tab
- NEON processing. */
void mul_tab_neon(float *t1, float *t2, float *tr) {
for (int i = 0; i < n; i+=4)
vst1q_f32(tr+i, vmulq_f32(vld1q_f32(t1+i), vld1q_f32(t2+i)));
}

int main() {
float t1[n], t2[n], tr[n];

/* fill tables with random values */
srand(1); rand_tab(t1); rand_tab(t2);// I repeat table multiplication function 1000000 times for measuring purposes:
for (int k=0; k < 1000000; k++)
mul_tab_standard(t1, t2, tr);  // switch to next line for comparison:
//mul_tab_neon(t1, t2, tr);
return 1;
}

Я запускаю следующую команду для компиляции:
g ++ -mfpu = neon -ffast-math neon_test.cpp

Мой процессор: процессор ARMv7, версия 0 (v7l)

У вас есть идеи, как мне добиться более значительного ускорения?

5

Решение

Cortex-A8 и Cortex-A9 могут выполнять только два умножения SP FP за такт, поэтому вы можете максимально увеличить производительность этих (самых популярных) процессоров в два раза. На практике процессоры ARM имеют очень низкий IPC, поэтому желательно максимально развернуть циклы. Если вы хотите максимальной производительности, напишите в ассемблере: генератор кода gcc для ARM нигде не так хорош, как для x86.

Я также рекомендую использовать специфичные для CPU опции оптимизации: «-O3 -mcpu = cortex-a9 -march = armv7-a -mtune = cortex-a9 -mfpu = neon -mthumb» для Cortex-A9; для Cortex-A15, Cortex-A8 и Cortex-A5 замените -mcpu = -mtune = cortex-a15 / a8 / a5 соответственно. gcc не имеет оптимизации для процессоров Qualcomm, поэтому для Qualcomm Scorpion используйте параметры Cortex-A8 (а также разверните даже больше, чем вы обычно делаете), а для Qualcomm Krait попробуйте параметры Cortex-A15 (вам потребуется последняя версия gcc, которая поддерживает Это).

5

Другие решения

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

Скомпилировано с gcc версии 4.4.3 и опциями -c -std = c99 -mfpu = неон -O3 и сбрасывается с помощью objdump, это часть цикла mul_tab_neon

000000a4 <mul_tab_neon>:
ac:   e0805003    add r5, r0, r3
b0:   e0814003    add r4, r1, r3
b4:   e082c003    add ip, r2, r3
b8:   e2833010    add r3, r3, #16
bc:   f4650a8f    vld1.32 {d16-d17}, [r5]
c0:   f4642a8f    vld1.32 {d18-d19}, [r4]
c4:   e3530e19    cmp r3, #400    ; 0x190
c8:   f3400df2    vmul.f32    q8, q8, q9
cc:   f44c0a8f    vst1.32 {d16-d17}, [ip]
d0:   1afffff5    bne ac <mul_tab_neon+0x8>

и это часть цикла mul_tab_standard

00000000 <mul_tab_standard>:
58:   ecf01b02    vldmia  r0!, {d17}
5c:   ecf10b02    vldmia  r1!, {d16}
60:   f3410db0    vmul.f32    d16, d17, d16
64:   ece20b02    vstmia  r2!, {d16}
68:   e1520003    cmp r2, r3
6c:   1afffff9    bne 58 <mul_tab_standard+0x58>

Как вы можете видеть в стандартном случае, компилятор создает гораздо более жесткий цикл.

2

По вопросам рекламы [email protected]