【问题标题】:Fastest way to multiply a static sized vector with a static sized constant non-square matrix in C, producing a 15-element output vector在 C 中将静态大小的向量与静态大小的常量非方阵相乘的最快方法,生成一个 15 元素的输出向量
【发布时间】:2020-11-21 10:52:55
【问题描述】:

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

然后我们有一个uint8_t 向量X,其行大小为100000。AX 具有静态大小,并且永远不会改变大小。

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)

【问题讨论】:

  • 什么时候设置A值?
  • @Ôrel 是的。它们都是价值观。
  • 我认为@Ôrel 的意思是询问是否有许多 A 值是 0.0,使其变得稀疏。 (或者如果有任何其他限制,例如 A 值为 0 或 1,因此行 x 列点积减少为掩码总和。)
  • @PeterCordes No. 0,0 是矩阵的索引。
  • @PeterCordes 我在问题中添加了float

标签: c performance matrix optimization matrix-multiplication


【解决方案1】:

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


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

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

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


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

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


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

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

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

【讨论】:

  • 哇!谢谢!您认为 SIMD 也适用于嵌入式系统吗?我说的是不能有大型应用程序和库的系统。
  • 我来“快速”乘法的最快方法是这段代码:
  • @DanielMårtensson:这取决于您使用的 CPU。您可以获得用于启用了 Neon (SIMD) 的嵌入式应用程序的 ARM CPU。你的问题没有说明你的目标是什么系统,所以就像我说的那样,我必须做出假设。
  • 无论如何我都喜欢你的回答。嗯..好吧,假设我们对硬件一无所知,除非数据可以适合处理器。我会使用我链接的 mul.c 文件,因为它是这种情况下的最佳方式?
猜你喜欢
  • 2016-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-30
  • 1970-01-01
相关资源
最近更新 更多