繁体   English   中英

Fastest way to multiply a static sized vector with a static sized constant non-square matrix in C, producing a 15-element output vector

[英]Fastest way to multiply a static sized vector with a static sized constant non-square matrix in C, producing a 15-element output vector

我有一个大型float矩阵A ,列长约为 100000,行长约为 15。

然后我们有一个行大小为 100000 的uint8_t向量AX都有X大小并且永远不会改变大小。

X可以更改其值,但A保持不变。

那么在 C 中计算A*X并产生 15 元素乘积向量的最绝对最快的方法是什么? 编写这样的东西而不是使用for循环是一种好方法吗?

 A(0,0)*X(0) + A(0,1)*X(1) + A(0,2)*X(2) + ... +  A(0,n)*X(n)
 A(1,0)*X(0) + A(1,1)*X(1) + A(1,2)*X(2) + ... +  A(1,n)*X(n)
 ......
 A(m,0)*X(0) + A(m,1)*X(1) + A(m,2)*X(2) + ... +  A(m,n)*X(n)

我假设你的矩阵是密集的,而不是稀疏的。 (主要不是0.0 )。 此外,它主要不是由0.01.0元素组成; 如果是这种情况,请将其转换为 bitmap ,您可以将其用于向量的掩码总和。


我假设这些是float值或double精度值,并且您希望在带有 SIMD 的典型现代机器(如 x86-64 或 AArch64)上运行它。 循环可能更好,因为您需要编译器自动矢量化以获得最大性能,并且循环比完全展开的代码更有可能。

您可能希望将X[]的每个 SIMD 块与 4 个左右的A[][]数据块中的每一个一起使用,因此X[]只需加载到寄存器中总共 4 次。 A[][]的每一行仅读取一次,因此A[][]无法重复使用数据。

缓存阻塞还可以将必须将 X[] 数据提取到 L1d 缓存中的次数减少到总共 1 次。 但是您可能不想编写一个循环并行执行 15 次求和。 从 A 获取 15 个流可能是个坏主意,而 x86-64(没有 AVX512)只有 16 个 SIMD 寄存器,因此除非您仔细手动进行矢量化,否则编译器可能会将矢量累加器溢出到堆栈并引入存储转发瓶颈。


不要完全展开:代码缓存未命中会比循环开销造成更大的伤害。

编译器通常不会将直线代码回滚到循环中,即使这样会更好。 所以你会得到一大块没有分支的asm。 CPU 必须从 memory 中获取代码,而不是从 L1 指令缓存(或 uop 缓存或循环缓冲区)中重用相同的循环体,这会消耗与数据带宽竞争的带宽。


在实践中,您应该调用 BLAS 库 function:它将使用 SIMD 针对安装在系统中的特定 CPU 进行大量优化。

或者不是,根据上次更新, Xuint8_t X[] 我怀疑是否有 BLAS 库可以即时从uint8_t转换为float ,但这可能是您想要节省 memory 带宽的原因,而不是单独转换为 tmp float向量。 虽然这样做+调用一个好的BLAS function 仍然比糟糕的向量化代码更好,如果你的编译器不能很好地处理你的纯C循环。

展开以多次使用X数据的每个 SIMD 向量将非常好,可以将转换开销分摊到更多次。 就像可能在 8 点之前展开。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM