Использование ARM NEON медленнее в простой задаче сложения - PullRequest
0 голосов
/ 07 января 2020

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

float A[] = {1,2,3,4}; 
float B[] = {2,3,4,5}; 
float32x4_t v1;
float32x4_t v2;

int counter = 1000000;

while(counter--){
     v1 = vld1q_f32(A);
     v2 = vld1q_f32(B);
     v = vaddq_f32(v1,v2);

     vst1q_f32(A,v);
     // A[0] = A[0]+B[0]; // regular implementation
     // A[1] = A[1]+B[1]; // regular implementation 
     // A[2] = A[2]+B[2]; // regular implementation
     // A[3] = A[3]+B[3]; // regular implementation

 }

Я искал причину, так что я думаю, это из-за конвейера в порядке, и эта простая задача вызывает остановку процессора? Но кто-нибудь может помочь объяснить более подробно? И есть ли способ улучшить производительность этой реализации NEON? Или лучше использовать обычную реализацию, чем NEON, когда сталкиваешься с такой простой задачей? Спасибо.

Ответы [ 2 ]

3 голосов
/ 08 января 2020

Ваша подпрограмма тестирования полностью испорчена, чтобы начинаться с:

Поскольку все входные данные четко видны компилятору во время сборки, компилятор просто сгенерирует машинные коды, подобные приведенному ниже:

A[0] = 3.0f;
A[1] = 5.0f;
A[2] = 7.0f;
A[3] = 9.0f;

Чтобы компиляторы не могли этого типа обмануть, вы должны скрыть ввод:

void myFunc_c(float *pA, float *pB, uint32_t count)
{
    if (count == 0) return;

    do {
        *pA++ += *pB++;
    } while (--count);
}

void myFunc_neon(float *pA, float *pB, uint32_t count)
{
    float32x4_t a, b;

    count >>= 2;
    if (count == 0) return;

    do {
        a = vld1q_f32(pA);
        b = vld1q_f32(pB);

        a = vaddq_f32(a, b);

        vst1q_f32(pA, a);

        pA += 4;
        pB += 4;

    } while (--count);    
}

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

Я думаю, что неоновая версия будет примерно в 3 раза быстрее.

1 голос
/ 07 января 2020

Если вы используете встроенные функции для загрузки / хранения, компилятор, похоже, оставляет их в покое.

Без встроенных функций он может оптимизировать загрузку / сохранение и использовать только регистры для промежуточных значений. Взгляните на сгенерированную сборку: https://www.godbolt.org/z/FvqKP3

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...