Обрабатывать в больших количествах по инструкции, чередовать загрузку / хранение и чередовать использование.В настоящее время эта функция удваивается (сдвигается влево) 56 uint.
void shiftleft56(const unsigned int* input, unsigned int* output)
{
__asm__ (
"vldm %0!, {q2-q8}\n\t"
"vldm %0!, {q9-q15}\n\t"
"vshl.u32 q0, q2, #1\n\t"
"vshl.u32 q1, q3, #1\n\t"
"vshl.u32 q2, q4, #1\n\t"
"vshl.u32 q3, q5, #1\n\t"
"vshl.u32 q4, q6, #1\n\t"
"vshl.u32 q5, q7, #1\n\t"
"vshl.u32 q6, q8, #1\n\t"
"vshl.u32 q7, q9, #1\n\t"
"vstm %1!, {q0-q6}\n\t"
// "vldm %0!, {q0-q6}\n\t" if you want to overlap...
"vshl.u32 q8, q10, #1\n\t"
"vshl.u32 q9, q11, #1\n\t"
"vshl.u32 q10, q12, #1\n\t"
"vshl.u32 q11, q13, #1\n\t"
"vshl.u32 q12, q14, #1\n\t"
"vshl.u32 q13, q15, #1\n\t"
// lost cycle here unless you overlap
"vstm %1!, {q7-q13}\n\t"
: "=r"(input), "=r"(output) : "0"(input), "1"(output)
: "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7",
"q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "memory" );
}
Что важно помнить для оптимизации Neon ... Она имеет два конвейера, один для загрузки / сохранения (с 2 очередями инструкций - один ожидающий иодин запуск - обычно по 3-9 циклов каждый) и один для арифметических операций (с 2 конвейерами команд, один выполняется и один сохраняет результаты).Пока вы удерживаете эти два конвейера занятыми и чередуете свои инструкции, это будет работать очень быстро.Более того, если у вас есть инструкции ARM, пока вы остаетесь в регистрах, вам никогда не придется ждать выполнения NEON, они будут выполняться одновременно (до 8 инструкций в кеше)!Таким образом, вы можете установить некоторую базовую логику цикла в инструкциях ARM, и они будут выполняться одновременно.
Ваш исходный код также использовал только одно значение регистра из 4 (регистр q имеет 4 32-битных значения).3 из них получали операцию удвоения без видимой причины, поэтому вы работали в 4 раза медленнее, чем могли бы.
Что было бы лучше в этом коде, так это выполнить этот цикл, обработав их встроеннымидобавление vldm %0!, {q2-q8}
после vstm %1!
... и так далее.Вы также видите, что я жду еще 1 инструкцию перед отправкой ее результатов, поэтому каналы никогда не ждут чего-то другого.Наконец, обратите внимание на !
, это означает постинкремент.Таким образом, он читает / записывает значение, а затем автоматически увеличивает указатель из регистра.Я предлагаю вам не использовать этот регистр в коде ARM, чтобы он не зависал в своих собственных конвейерах ... держите ваши регистры отдельно, у вас должна быть избыточная переменная count
на стороне ARM.
Последняя часть... то, что я сказал, может быть правдой, но не всегда.Это зависит от текущей версии Neon.Сроки могут измениться в будущем или не всегда были такими.Это работает для меня, мммм.