当前位置: 首页 > 知识库问答 >
问题:

利用扭曲比特的快速整数矩阵乘法

都博裕
2023-03-14

我想问的是,是否有可能用按位运算大大改进整数矩阵乘法。矩阵很小,元素是小的非负整数(小表示最多20个)。

为了保持我们的注意力集中,让我们非常具体,假设我有两个3x3矩阵,有整数条目0<=x<15。

下面这个朴素的C++实现被执行了一百万次,使用linuxtime来衡量,它的性能大约为1秒。

#include <random>

int main() {
//Random number generator
std::random_device rd;
std::mt19937 eng(rd());
std::uniform_int_distribution<> distr(0, 15);

int A[3][3];
int B[3][3];
int C[3][3];
for (int trials = 0; trials <= 1000000; trials++) {
    //Set up A[] and B[]
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            A[i][j] = distr(eng);
            B[i][j] = distr(eng);
            C[i][j] = 0;
        }
    }
    //Compute C[]=A[]*B[]
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            for (int k = 0; k < 3; ++k) {
                C[i][j] = C[i][j] + A[i][k] * B[k][j];
            }
        }
    }
}
return 0;
}

备注:

  1. 矩阵不一定是稀疏的。
  2. 类似Strassen的注释在这里没有帮助。
  3. 让我们尽量不使用间接观察,即在这个具体问题中,矩阵a[]B[]可以被编码为单个64位整数。想想稍微大一点的矩阵会发生什么。
  4. 计算是单线程的。

相关:二进制矩阵乘法,比特旋转黑客和什么是游戏的最佳算法2048?

共有1个答案

华知
2023-03-14

你链接的问题是关于一个矩阵,其中每个元素都是一个位。对于一位值ABA*BA&B完全等效。

对于添加2位元素,基本上从头开始添加,使用XOR(carryless-add),然后使用and、shift和mask off carry跨元素边界生成carry可能是合理的(而且比解包更快)。

第3位将需要检测当添加进位时产生另一进位。我认为与使用SIMD相比,即使是模拟3位加法器或乘法器也不会是一个胜利。如果没有SIMD(即在带有uint64_t的纯C中),它可能是有意义的。对于add,您可以尝试使用普通的add,然后尝试撤消元素边界之间的进位,而不是通过异或/与/移位操作自己构建一个加法器。

如果您有很多这样的小矩阵,以压缩的形式(例如,打包的4位元素)将它们存储在内存中可以帮助增加缓存占用空间/内存带宽。4bit元素很容易解包,使每个元素都在向量的一个单独的字节元素中。

否则,每字节存储一个矩阵元素。从那里,根据目标SIMD指令集提供的元素大小,如果需要,您可以轻松地将它们解包为每个元素16bit或32bit。您可以将局部变量中的一些矩阵保留为未打包格式,以便在乘法中重用,但将它们打包为每个元素的4bits以存储在数组中。

编译器在x86的标量C代码中使用uint8_t来解决这一问题。参见@Richard's Answer的评论:gcc和clang都喜欢对uint8_t使用mul r8,这迫使它们将数据移入eax(一个操作数乘法的隐式输入/输出),而不是使用imul r32,r32并忽略目标寄存器低8位之外的垃圾。

uint8_t版本实际上比uint16_t版本运行得慢,尽管它的缓存占用面积只有uint8_t版本的一半。

Intel SSSE3有一个向量字节乘法器,但仅与相邻元素相加。使用它需要将矩阵拆分成一个向量,在行之间加上一些零或其他东西,这样就不会得到一行的数据和另一行的数据混在一起。幸运的是,pshufb可以零元素,也可以复制它们。

更有用的是SSE2pmaddwd,如果您在一个单独的16bit向量元素中解包到每个矩阵元素。因此,给定一个向量中的一行和另一个向量中的转置列,pmaddwd(_mm_madd_epi16)只需要一个水平的add,就可以得到C[i][j]所需的点积结果。

您可以将多个pmaddwd结果打包到一个向量中,这样就可以一次性存储c[i][0..2]

 类似资料:
  • 在课堂上,我必须为稀疏矩阵编写自己的线性方程求解器。我可以自由地使用任何类型的数据结构为稀疏矩阵,我必须实现几个解决方案,包括共轭梯度。 谢了!

  • C++:15秒(源) Python:6分13秒(来源) C++:45分钟(源) 蟒蛇:10小时后被杀死(来源) 为什么Strassen矩阵乘法比标准矩阵乘法慢得多? null null null

  • 做一些类似的事情 使用多个内核,运行良好。 所以,如果我要做整数矩阵乘法,我得做下面的一个: 使用numpy慢得让人痛苦的并庆幸我可以保留8位整数。 使用Scipy的并使用4倍内存。 使用numpy的并且只使用2倍内存,但要注意的是,在float16数组上的速度要比在float32数组上慢得多,比int8慢得多。 为多线程整数矩阵乘法找到一个优化的库(其实Mathematica就是这么做的,但我更

  • 主要内容:逐元素矩阵乘法,矩阵乘积运算,矩阵点积矩阵乘法是将两个矩阵作为输入值,并将 A 矩阵的行与 B 矩阵的列对应位置相乘再相加,从而生成一个新矩阵,如下图所示: 注意:必须确保第一个矩阵中的行数等于第二个矩阵中的列数,否则不能进行矩阵乘法运算。 图1:矩阵乘法 矩阵乘法运算被称为向量化操作,向量化的主要目的是减少使用的 for 循环次数或者根本不使用。这样做的目的是为了加速程序的计算。 下面介绍 NumPy 提供的三种矩阵乘法,从而进一步

  • 问题内容: 在numpy中,我有N个3x3矩阵的数组。这将是我如何存储它们的示例(我正在提取内容): 我也有一个由3个向量组成的数组,这将是一个示例: 我似乎无法弄清楚如何通过numpy将它们相乘,从而实现如下效果: 与的形状(在投射到阵列)是。但是,由于速度的原因,列表实现是不可能的。 我尝试了各种换位的np.dot,但最终结果没有得到正确的形状。 问题答案: 使用 脚步 : 1)保持第一根轴对

  • 我想使用寄存器(逐行信息)通过向量算法创建矩阵乘法。打开外循环4次我有空洞matvec_XMM(双* a,双* x,双* y,整数n,整数磅)函数的问题,它返回了不好的结果,这是算法wchich我必须使用: 它是ma代码: