Как использовать умножение и накопление встроенных функций в ARM Cortex-a8? - PullRequest
13 голосов
/ 13 июля 2010

как использовать свойства Multiply-Accumulate, предоставляемые GCC?

float32x4_t vmlaq_f32 (float32x4_t , float32x4_t , float32x4_t);

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

Справка !!!

Ответы [ 3 ]

20 голосов
/ 14 июля 2010

Проще говоря, команда vmla делает следующее:

struct 
{
  float val[4];
} float32x4_t


float32x4_t vmla (float32x4_t a, float32x4_t b, float32x4_t c)
{
  float32x4 result;

  for (int i=0; i<4; i++)
  {
    result.val[i] =  b.val[i]*c.val[i]+a.val[i];
  }

  return result;
}

И все это компилируется в отдельную инструкцию ассемблера: -)

Вы можете использовать этот встроенный NEON-ассемблер среди других вещейв типичных матричных умножениях 4x4 для 3D-графики, таких как:

float32x4_t transform (float32x4_t * matrix, float32x4_t vector)
{
  /* in a perfect world this code would compile into just four instructions */
  float32x4_t result;

  result = vml (matrix[0], vector);
  result = vmla (result, matrix[1], vector);
  result = vmla (result, matrix[2], vector);
  result = vmla (result, matrix[3], vector);

  return result;
}

Это экономит пару циклов, потому что вам не нужно добавлять результаты после умножения.Это добавление используется так часто, что накопление hsa с множественным накоплением в наши дни становится мейнстримом (даже x86 добавил их в какой-то недавний набор инструкций SSE).

Также стоит упомянуть: такие операции умножения с накоплением очень распространено в приложениях линейной алгебры и цифровой обработки сигналов.ARM был очень умным и реализовал fast-path внутри NEON-Core Cortex-A8.Этот быстрый путь запускается, если первый аргумент (накопитель) инструкции VMLA является результатом предшествующей инструкции VML или VMLA.Я мог бы вдаваться в подробности, но в двух словах такая серия инструкций выполняется в четыре раза быстрее, чем серия VML / VADD / VML / VADD.

Взгляните на мое простое умножение матриц: я сделал именно это.Благодаря такому быстрому пути он будет работать примерно в четыре раза быстрее, чем реализация, написанная с использованием VML и ADD вместо VMLA.

8 голосов
/ 13 июля 2010

Google'd для vmlaq_f32, появился справочник по инструментам компилятора RVCT .Вот что он говорит:

Vector multiply accumulate: vmla -> Vr[i] := Va[i] + Vb[i] * Vc[i]
...
float32x4_t vmlaq_f32 (float32x4_t a, float32x4_t b, float32x4_t c);

AND

Следующие типы определены для представления векторов.Векторные типы данных NEON названы в соответствии со следующим шаблоном: x_t Например, int16x4_t - это вектор, содержащий четыре дорожки, каждая из которых содержит 16-разрядное целое число со знаком.В таблице E.1 перечислены векторные типы данных.

IOW, возвращаемое значение из функции будет вектором, содержащим 4 32-битных числа с плавающей запятой, и каждый элемент вектора вычисляется путем умножения соответствующих элементов b и c и добавлениясодержимое a.

HTH

1 голос
/ 29 февраля 2012
result = vml (matrix[0], vector);
result = vmla (result, matrix[1], vector);
result = vmla (result, matrix[2], vector);
result = vmla (result, matrix[3], vector);

Эта последовательность не сработает. Проблема в том, что компонент x накапливает только x, модулированный строками матрицы, и может быть выражен как:

result.x = vector.x * (matrix[0][0] + matrix[1][0] + matrix[2][0] + matrix[3][0]);

...

Правильная последовательность будет:

result = vml (matrix[0], vector.xxxx);
result = vmla(result, matrix[1], vector.yyyy);

...

NEON и SSE не имеют встроенного выбора для полей (для этого потребуется 8 битов в кодировке команд на каждый векторный регистр). Например, GLSL / HLSL имеет такие возможности, поэтому большинство графических процессоров также имеют.

Альтернативный способ добиться этого будет:

result.x = dp4(vector, matrix[0]);
result.y = dp4(vector, matrix[1]);

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

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

В противном случае код выглядит хорошо. =)

...