【发布时间】:2021-11-20 08:16:20
【问题描述】:
我希望使用 AVX-1 或更早的指令来加速这个矩阵向量乘积:
// a is an array N columns of length M each
// b is length N
// c is length M
//
// M % 32 == N % 32 == 0
// all memory is nicely aligned and unaliased
void mat_vec_prod(const int8_t** a, const uint8_t* b, int16_t* c) {
for(int i = 0; i < M; ++i) {
c[i] = 0;
for(int j = 0; j < N; ++j)
c[i] += int16_t(a[j][i]) * int16_t(b[j]);
}
}
(我知道交换循环值得考虑)
内在函数 _mm_maddubs_epi16 和 _mm_maddubs_pi16 可以帮助处理 uint8 x int8 点积,但在我的例子中,矩阵的布局很尴尬,它是指向列(而不是行)的指针数组。
一种可能性是加载a 的8x8 块,然后将它们转置并乘以b 的片段。 (我在 8x8 字节矩阵转置上找到了 this thread)。但是,这必须使用_mm_maddubs_pi16,它的吞吐量只有_mm_maddubs_epi16 的一半。
我的问题是:是否值得尝试加载和转置 16x16 补丁,或者我会用完 xmm 寄存器?我的策略应该是什么?
【问题讨论】:
-
可以使用AVX2指令吗?有些人说 AVX 时的意思是 AVX/AVX2。
-
@fuz 不,只是 AVX。请编辑,如果有办法使这一点更清楚。
-
呃...针对 Sandy Bridge 或这一代 AMD 进行优化很烦人。让我们看看...
-
我将
[v]punpcklbwa[j][i:i+7]和a[j+1][i:i+7]的两个向量并使用[v]pmaddubsw乘以{b[j], b[j+1],b[j], b[j+1],...}(然后累积这些结果)。根据实际大小,您将需要某种阻塞(即,不要为每次添加重新加载c子向量。)如果没有vpbroadcastw,创建b-multiplier 有点烦人,我猜是 @ 987654339@ 后跟[v]pshufd应该可以解决问题。您应该将b-multiplier 重复用于累积c的尽可能多的寄存器。 -
@chtz 我实现了你的建议(至少按照我的理解)。比原始版本快 6 倍,可能会影响内存带宽。非常感谢!