【问题标题】:uint32_t * uint32_t = uint64_t vector multiplication with gccuint32_t * uint32_t = uint64_t 向量乘法与 gcc
【发布时间】:2020-03-09 07:06:00
【问题描述】:

我正在尝试将uint32_t 的向量相乘,从而在 gcc 中将完整的 64 位结果生成为 uint64_t 向量。我期望的结果是 gcc 发出一个 VPMULUDQ 指令。但是 gcc 作为代码输出的内容是对源向量的单个 uint32_t 进行可怕的洗牌,然后是完整的 64*64=64 乘法。这是我尝试过的:

#include <stdint.h>

typedef uint32_t v8lu __attribute__ ((vector_size (32)));
typedef uint64_t v4llu __attribute__ ((vector_size (32)));

v4llu mul(v8lu x, v8lu y) {
    x[1] = 0; x[3] = 0; x[5] = 0; x[7] = 0;
    y[1] = 0; y[3] = 0; y[5] = 0; y[7] = 0;
    return (v4llu)x * (v4llu)y;
}

第一个屏蔽掉 uint32_t 向量中不需要的部分,希望 gcc 能够优化掉 64*64=64 乘法中不需要的部分,然后发现屏蔽也毫无意义。没有这样的运气。

v4llu mul2(v8lu x, v8lu y) {
    v4llu tx = {x[0], x[2], x[4], x[6]};
    v4llu ty = {y[0], y[2], y[4], y[6]};
    return tx * ty;
}

在这里,我尝试从头开始创建一个 uint64_t 向量,其中仅包含使用过的部件集。 gcc 应该再次看到每个 uint64_t 的前 32 位为 0,而不是执行完整的 64*64=64 乘法。相反,会发生大量提取和放回值,并且 64*64=64 相乘。

v4llu mul3(v8lu x, v8lu y) {
    v4llu t = {x[0] * (uint64_t)y[0], x[2] * (uint64_t)y[2], x[4] * (uint64_t)y[4], x[6] * (uint64_t)y[6]};
    return t;
}

让我们通过将部分相乘来构建结果向量。也许 gcc 看到它可以使用VPMULUDQ 来实现这一点。不走运,它回落到 4 个IMUL 操作码。

有没有办法告诉 gcc 我想要它做什么(32*32=64 乘以所有完美放置的东西)?

注意:内联汇编或内在不是答案。手工编写操作码显然是可行的。但是我必须为许多目标架构和功能集编写不同版本的代码。我希望 gcc 能够理解问题并从单个源代码中生成正确的解决方案。

【问题讨论】:

  • 如果你只是想知道如何让 GCC 做你想做的事,为什么不使用 @Ben 提出的内在函数呢?依靠创建某种代码模式似乎很脆弱,您现在使用的 GCC 版本恰好可以识别并发出您想要的代码。如果您想知道它会起作用,请使用明确指定您的意图的内在函数。
  • @GoswinvonBrederlow:为什么内在不是答案?如果它可以满足您的需求,为什么不使用它呢?
  • mulmul2 使用 clang 优化:godbolt.org/z/d3MAay, mul3 不等效,因为它需要将结果截断为 32 位。我猜你的选择是:a)使用clang,b)使用内在函数,c)为gcc提供一个补丁来正确优化这个(或提交一个错误并希望其他人修复它)。
  • @Ben:标准的可移植内在函数是_mm256_mul_epu32,由immintrin.h定义
  • @GoswinvonBrederlow:“不是我想要的”和“如果我想使用内在函数,我会这样做”不是正当的理由。 “因为我们需要支持许多不同的目标架构,并且为每个架构编写单独的代码成本太高”是。根据实际项目要求,而不是“想要”,编辑问题以陈述您的全部要求。

标签: c gcc vectorization avx2 gcc9


【解决方案1】:

正如 chtz 在 cmets 中所指出的,mul1 和 mul2 都通过 clang 进行了优化。与 mul3 类似但使用 for 循环的代码也将得到优化(但效果不佳)。

所以在我看来,表达代码应该做什么的语法是正确的,而 gcc 到目前为止只是缺乏正确优化它的聪明才智。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多