C - 浮点矩阵和稀疏布尔值矩阵之间的可移植快速点积
C - portable fast dot product between a float matrix and a sparse boolean value matrix
我正在用 C 语言开发一个尖峰神经网络项目,其中尖峰是布尔值。现在我已经构建了一个自定义位矩阵类型来表示尖峰矩阵。
我经常需要位矩阵的点积和相同大小的单精度浮点数矩阵,所以我想知道我应该如何加快速度?
后面还需要做浮点矩阵和位矩阵的逐点乘法
我现在的计划只是循环遍历 with 和 if 语句以及 bitshift。我想加快速度。
float current = 0;
for (int i = 0; i < n_elem; i++, bit_vec >>= 1) {
if (bit_vec & 1)
current += weights[i];
}
我不一定需要使用位向量,它也可以用其他方式表示。我在这里看到了其他答案,但它们是特定于硬件的,我正在寻找可以移植的东西。
我也没有使用任何 BLAS 函数,主要是因为我从不对两个浮点数进行操作。我应该吗?
谢谢。
bit_vec >>= 1
和 current += weights[i]
指令导致循环携带依赖性,这肯定会阻止编译器生成快速实现(并且也会阻止处理器有效地执行它)。
您可以通过展开循环 来解决这个问题。此外,大多数主流编译器不够智能,因此要优化条件 en 使用大多数体系结构上可用的 blend 指令。 条件分支很慢,尤其是当它们不容易预测时(这当然是你的情况)。您可以使用乘法 so 来帮助编译器生成更好的指令。这是结果:
const unsigned int blockSize = 4;
float current[blockSize] = {0.f};
int i;
for (i = 0; i < n_elem-blockSize+1; i+=blockSize, bit_vec >>= blockSize)
for(int j = 0 ; j < blockSize ; ++j)
current[j] += weights[i] * (bit_vec >> j);
for (; i < n_elem; ++i, bit_vec >>= 1)
if (bit_vec & 1)
current[0] += weights[i];
float sum = 0.f;
for(int j = 0 ; j < blockSize ; ++j)
sum += current[j];
假定 n_elem
相对较大,此代码应该更快。由于像 GCC 和 Clang 这样的编译器无法 auto-vectorize 它应该仍然远非高效。这很可悲,因为使用 SIMD 指令(如 SSE、AVX、Neon 等)会快好几倍。话虽这么说,这正是人们使用 non-portable 代码的原因:手动使用高效指令,因为编译器在 non-trivial 情况下通常无法做到这一点。
我正在用 C 语言开发一个尖峰神经网络项目,其中尖峰是布尔值。现在我已经构建了一个自定义位矩阵类型来表示尖峰矩阵。
我经常需要位矩阵的点积和相同大小的单精度浮点数矩阵,所以我想知道我应该如何加快速度?
后面还需要做浮点矩阵和位矩阵的逐点乘法
我现在的计划只是循环遍历 with 和 if 语句以及 bitshift。我想加快速度。
float current = 0;
for (int i = 0; i < n_elem; i++, bit_vec >>= 1) {
if (bit_vec & 1)
current += weights[i];
}
我不一定需要使用位向量,它也可以用其他方式表示。我在这里看到了其他答案,但它们是特定于硬件的,我正在寻找可以移植的东西。
我也没有使用任何 BLAS 函数,主要是因为我从不对两个浮点数进行操作。我应该吗?
谢谢。
bit_vec >>= 1
和 current += weights[i]
指令导致循环携带依赖性,这肯定会阻止编译器生成快速实现(并且也会阻止处理器有效地执行它)。
您可以通过展开循环 来解决这个问题。此外,大多数主流编译器不够智能,因此要优化条件 en 使用大多数体系结构上可用的 blend 指令。 条件分支很慢,尤其是当它们不容易预测时(这当然是你的情况)。您可以使用乘法 so 来帮助编译器生成更好的指令。这是结果:
const unsigned int blockSize = 4;
float current[blockSize] = {0.f};
int i;
for (i = 0; i < n_elem-blockSize+1; i+=blockSize, bit_vec >>= blockSize)
for(int j = 0 ; j < blockSize ; ++j)
current[j] += weights[i] * (bit_vec >> j);
for (; i < n_elem; ++i, bit_vec >>= 1)
if (bit_vec & 1)
current[0] += weights[i];
float sum = 0.f;
for(int j = 0 ; j < blockSize ; ++j)
sum += current[j];
假定 n_elem
相对较大,此代码应该更快。由于像 GCC 和 Clang 这样的编译器无法 auto-vectorize 它应该仍然远非高效。这很可悲,因为使用 SIMD 指令(如 SSE、AVX、Neon 等)会快好几倍。话虽这么说,这正是人们使用 non-portable 代码的原因:手动使用高效指令,因为编译器在 non-trivial 情况下通常无法做到这一点。