我目前正在阅读一篇关于github的文章,内容是使用Clang的扩展向量语法进行性能优化。作者给出了以下代码片段:
下面的模板代码实现了计算矩阵C中大小为regA x regB的补丁的最内部循环。代码从matrixA加载regA标量,从矩阵B加载regB SIMD宽度向量。程序使用Clang的扩展向量语法。
/// Compute a RAxRB block of C using a vectorized dot product, where RA is the
/// number of registers to load from matrix A, and RB is the number of registers
/// to load from matrix B.
template <unsigned regsA, unsigned regsB>
void matmul_dot_inner(int k, const float *a, int lda, const float *b, int ldb,
float *c, int ldc) {
float8 csum[regsA][regsB] = {{0.0}};
for (int p = 0; p < k; p++) {
// Perform the DOT product.
for (int bi = 0; bi < regsB; bi++) {
float8 bb = LoadFloat8(&B(p, bi * 8));
for (int ai = 0; ai < regsA; ai++) {
float8 aa = BroadcastFloat8(A(ai, p));
csum[ai][bi] += aa * bb;
}
}
}
// Accumulate the results into C.
for (int ai = 0; ai < regsA; ai++) {
for (int bi = 0; bi < regsB; bi++) {
AdduFloat8(&C(ai, bi * 8), csum[ai][bi]);
}
}
}
下面的代码,最让我困惑。我阅读了整篇文章,理解了使用阻塞和计算小补丁背后的逻辑,但我不能完全理解这段代码意味着什么:
// Perform the DOT product.
for (int bi = 0; bi < regsB; bi++) {
float8 bb = LoadFloat8(&B(p, bi * 8)); //the pointer to the range of values?
for (int ai = 0; ai < regsA; ai++) {
float8 aa = BroadcastFloat8(A(ai, p));
csum[ai][bi] += aa * bb;
}
}
}
有人能详细说明这里面发生了什么吗?这篇文章可以找到这里
发布于 2020-07-05 02:38:04
文章的第二个注释链接到defs.h#L26,它将float8
类型定义为
typedef float float8 __attribute__((ext_vector_type(8)));
(类似于immintrin.h如何定义__m256
)。并定义了类似于_mm256_load_ps
和_mm256_set1_ps
的加载/广播功能。使用该标题,您应该能够编译本文中的代码。
见Clang的本机矢量文档。GNU本机矢量语法是获得重载*
运算符的好方法。我不知道clang的ext_vector_type
做什么,GCC/clang/ICC float __attribute__((vector_width(32)))
(32字节宽)不会。
这篇文章可以增加一个小部分来解释这一点,但它似乎更多地关注性能细节,而不是真正感兴趣地解释如何使用语法。
本文讨论的大部分内容是如何用SIMD向量手动矢量化匹配以提高缓存效率。从我给它的快速浏览来看,那部分看上去不错。
您可以使用多种方法来完成这些操作: GNU C本地向量或clang非常相似的“扩展”向量,或可移植的Intel内部组件。
https://stackoverflow.com/questions/62729814
复制相似问题