Встроенное в Clang расширение матрицы и вектора: эффективное умножение матрицы на вектор
Я пишу небольшое графическое 3D-приложение, чтобы узнать о векторных и матричных расширениях Clang (матрицы все еще разрабатываются, если я читаю правильные версии документа ).
Я не уверен, как написать наиболее эффективный код для умножения матрицы на вектор, используя этот тип. С использованием:
typedef float float4 __attribute__((ext_vector_type(4)));
typedef float m4x4 __attribute__((matrix_type(4, 4)));
В документе говорится (относительно индексов для доступа к элементам матрицы):
Первый указывает количество строк, а второй — количество столбцов.
Column
|
v
Row->| M00 M01 M02 M03 |
| M10 M11 M12 M13 |
| M20 M21 M22 X23 |
| M30 M31 M32 M33 |
Итак, я понимаю, что выполнение m[2][3] (где m — это m4x4) даст мне элемент, который я отметил X в матрице выше.
Затем (относительно того, как элементы расположены в памяти):
Элементы значения матричного типа располагаются по столбцам без заполнения.
Итак, из этой заметки я понял, что если бы я мог посмотреть, как элементы хранятся в памяти, я бы получил:
M00 M10 M20 M30 - M01 M11 M21 M31 - M02 M12 M22 M32 - M03 M13 X23 M33
Я правильно понял?
Имеет ли значение порядок, в котором мы обращаемся к элементам матрицы? (и правильно ли я делаю?)
Затем я предполагаю, что если бы я хотел быть эффективным в своем умножении mat-float4, мне нужно было бы получить доступ к элементам так, как они расположены в памяти, поэтому сделайте это:
m4x3 m;
float4 v = {0.2, 0.3, 0.4, 1};
float4 res = {
v.x * m[0][0] + v.y * m[1][0] + v.z * m[2][0] + v.w * m[3][0],
v.x * m[0][1] + v.y * m[1][1] + v.z * m[2][1] + v.w * m[3][1],
v.x * m[0][2] + v.y * m[1][2] + v.z * m[2][2] + v.w * m[3][2],
1 // ignore w element for now
}
Конечно, я должен загрузить правильные значения в m[0][0], m[0][1],... используя что-то вроде __builtin_matrix_column_major_load
.
Я слишком усложняю вещи, или здесь должен иметь значение порядок. Действительно ли приведенное выше уравнение лучше, чем:
float4 res = {
v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2] + v.w * m[0][3],
v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2] + v.w * m[1][3],
v.x * m[2][0] + v.y * m[2][1] + v.z * m[2][2] + v.w * m[2][3],
1 // ignore w element for now
}
(при условии, что я переставил элементы перед вызовом__builtin_matrix_column_major_load
.
Есть ли лучший способ сделать это?
Теперь я понимаю, что эти типы разрабатываются в данный момент. Тем не менее, я понимаю, что весь смысл этих типов в том, чтобы воспользоваться SIMD-инструкциями. Если я сделаю:
float4 a = {...};
float4 b = {...};
float4 c = a + b;
Затем добавьте 4 поплавкаa
к соответствующим 4 поплавкамb
происходит в одном цикле? Итак, что касается умножения mat-float4, поскольку я вызываю элементы float4 и m4x4 по отдельности в своем коде, кажется, что в этом конкретном случае я не воспользуюсь преимуществами какой-либо оптимизации?
Итак, мой второй вопрос: есть ли лучший способ сделать это?
- Должен ли я хранить матричные векторы в 4 float4 и вместо этого выполнять умножение float4 * float4?
- Я видел этот пост Умножение матрицы-вектора и матрицы-матрицы с использованием SSE , в котором приводится пример того, как добиться умножения мат-вектора с использованием инструкций SIMD. Кажется, это позволяет складывать элементы матрицы в
__m128
и используйте их, чтобы получить элементы матрицы, умноженные на элементы вектора, используя дополнительные инструкции SIMD, такие как_mm_add_ps
иmm_mul_ps
. - Должен ли я просто ждать, пока эта разработка станет более зрелой?
Любая обратная связь или совет будут очень признательны. Я делаю это как упражнение, чтобы узнать об этих новых встроенных типах.