Максимальная оптимизация поэлементного умножения с помощью сборки ARM NEON

Я оптимизирую поэлементное умножение двух одномерных массивов для двухъядерного процессора Cortex-A9. Linux работает на плате, и я использую компилятор GCC 4.5.2.

Итак, следующая моя встроенная функция ассемблера C ++. src1, src2 и dst выровнены по 16 байтов.

Обновление: тестируемый код:

void Multiply(
const float* __restrict__ src1,
const float* __restrict__ src2,
float* __restrict__ dst,
const unsigned int width,
const unsigned int height)
{
int loopBound = (width * height) / 4;
asm volatile(
".loop:                             \n\t""vld1.32  {q1}, [%[src1]:128]!      \n\t""vld1.32  {q2}, [%[src2]:128]!      \n\t""vmul.f32 q0, q1, q2                \n\t""vst1.32  {q0}, [%[dst]:128]!       \n\t""subs     %[lBound], %[lBound], $1  \n\t""bge      .loop                     \n\t":
:[dst] "r" (dst), [src1] "r" (src1), [src2] "r" (src2),
[lBound] "r" (loopBound)
:"memory", "d0", "d1", "d2", "d3", "d4", "d5
);
}

//The following function describes how to test the element wise multiplication
void Test()
{
const unsigned int width = 1024, height = 1024;
float* src1 __attribute__((aligned(16))) = new float[width * height];
float* src2 __attribute__((aligned(16))) = new float[width * height];
float* dst __attribute__((aligned(16))) = new float[width * height];
for(unsigned int i = 0; i < (width * height); i++)
{
src1[i] = (float)rand();
src2[i] = (float)rand();
}
Multiply(src1, src2, dst, width, height);

std::cout << dst[0] << std::endl;
}

Расчет значений 1024 * 1024 занимает ~ 0,016 с. (Два потока — каждый поток вычисляет половину массива). Наивно интерпретируя, расчет одной итерации занимает 122 цикла. Это кажется немного медленным. Но где узкое место?

Я даже попробовал pld команда для предварительной загрузки элементов в кэш L2, «разворачивания» цикла путем вычисления до 20 значений за итерацию и переупорядочивания инструкций, чтобы убедиться, что процессор не ожидает памяти. Я не получил такого ускорения (максимум на 0,001 с быстрее).

Есть ли у вас предложения по ускорению расчета?

8

Решение

Я не очень много знаю о НЕОН. Тем не менее, я думаю, что у вас есть зависимости данных, которые вызывают проблемы с производительностью. Я бы посоветовал вам прокачать цикл с некоторыми нагрузками, а затем поместить их между умножать а также хранить. я думаю хранить вероятно блокируется до умножать готово.

    asm volatile(
"vld1.32  {q1}, [%[src1]:128]!      \n\t""vld1.32  {q2}, [%[src2]:128]!      \n\t"".loop:                             \n\t""vmul.f32 q0, q1, q2                \n\t""vld1.32  {q1}, [%[src1]:128]!      \n\t""vld1.32  {q2}, [%[src2]:128]!      \n\t""vst1.32  {q0}, [%[dst]:128]!       \n\t""subs     %[lBound], %[lBound], $1  \n\t""bge      .loop                     \n\t":
:[dst] "r" (dst), [src1] "r" (src1), [src2] "r" (src2),
[lBound] "r" (loopBound)
:"memory", "d0", "d1", "d2", "d3", "d4", "d5
);

Таким образом, вы должны быть в состоянии параллельно нагрузки с умножением. Вам нужно будет перераспределить исходные массивы или изменить индекс цикла и сделать окончательное умножение и сохранение. Если операции NEON не влияют на коды условий, вы можете переупорядочить подписка а также и поместите его раньше.

Редактировать: На самом деле, Cortex A-9 Media Processing Engine Документ рекомендует чередовать инструкции ARM и NEON, поскольку они могут выполняться параллельно. Кроме того, инструкции NEON, кажется, устанавливают FPSCR а не АРМ CPSR так что переупорядочив subs уменьшит время выполнения. Вы можете также кэш выровняйте петлю.

1

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

Других решений пока нет …

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