gpt4 book ai didi

c - 如何从预处理器启用内在函数

转载 作者:行者123 更新时间:2023-12-02 04:48:39 27 4
gpt4 key购买 nike

我可以通过使用查找表找到 16 位值的第 n 个设置位,但是对于 32 位值,如果不将其分解并使用多个 LUT,则无法完成此操作。这在How to efficiently find the n-th set bit?中进行了讨论。这表明与此类似的用途

#include <immintrin.h>
//...
unsigned i = 0x6B5; // 11010110101
unsigned n = 4; // ^
unsigned j = _pdep_u32(1u << n, i);
int bitnum = __builtin_ctz(j)); // result is bit 7

我已经介绍了这个建议的循环方法

j = i;
for (k = 0; k < n; k++)
j &= j - 1;
return (__builtin_ctz(j));

针对 Nominal Animal 的答案中的位旋转代码的两种变体。旋转的分支变体是三个代码版本中最快的,但相差不大。然而,在我的实际代码中,使用__builtin_ctz的上述代码段速度更快。还有其他答案,例如来自 fuz谁也建议了我的发现:位旋转与循环方法花费的时间相似。

所以我现在想尝试使用 _pdep_u32 但无法识别 _pdep_u32。我读到gcc.gnu.org

The following built-in functions are available when -mbmi2 is used.
...
unsigned int _pdep_u32 (unsigned int, unsigned int)
unsigned long long _pdep_u64 (unsigned long long, unsigned long long)
...

但是我使用的是在线编译器,无法访问这些选项。

有没有办法启用预处理器的选项?

有很多关于从命令行选项控制预处理器的 Material ,但我找不到如何反过来做。

最终我想使用 64 位版本 _pdep_u64

最佳答案

除了内联 asm 之外,gcc/clang 绝不会发出未通过命令行选项、函数属性或编译指示启用的 asm 指令。

如果您无法像普通人一样在命令行上使用 GCC 目标选项(例如 -march=native 启用编译主机支持的所有内容,并调整对于该机器),您可以改为使用 a pragma设置特定于目标的选项。但它的效果并不是很好;看来#pragma target("arch=skylake")打破了 GCC 的 immintrin.h (如果您将编译指示放在包含之前),显然是尝试在未启用 AVX512 的情况下编译 AVX512 函数。 The GCC docs do show examples of using "arch=core2" 作为函数属性,甚至 "sse4.1,arch=core2"

您可以设置target("bmi2,tune=skylake")但不会破坏任何内容(在 #include <immintrin.h> 之前或之后。(与 arch= 不同, tune= 不启用 ISA 扩展,只会影响代码生成选择。)

#include <immintrin.h>
// #pragma GCC target ("arch=haswell") // also broken, disables BMI2??

#pragma GCC target ("bmi2,tune=skylake") // this can be after immintrin.h

int foo(unsigned x) {
// unsigned i = 0x6B5; // 11010110101
unsigned i = x;
unsigned n = 4; // ^
unsigned j = _pdep_u32(1u << n, i);
int bitnum = __builtin_ctz(j); // result is bit 7
return bitnum;
}

int constprop() { // check that GCC can still "understand" the intrinsic
return foo(0x6B5);
}

<强> compiles cleanly on Godbolt -O3 ,否-m选项。

foo:
mov r8d, edi
mov edi, 16
pdep edi, edi, r8d
bsf eax, edi
ret

constprop:
mov eax, 7
ret
<小时/>

如果您在获取immintrin.h时遇到困难要定义正确的包装器,您可以查看 GCC 的 header 以了解它们是如何定义的。例如_pdep_u32__builtin_ia32_pdep_si 的包装(单个整数)并且 u64 是 __builtin_ia32_pdep_di 的包装器(双整数)。

这些__builtin即使没有 header ,函数也始终由 GCC 定义,但这些 ia32内置函数只能在具有兼容目标选项的函数中使用。没有像 __builtin_popcount 这样的后备方案对于不支持该指令的目标。

但似乎至少对于 BMI2 指令来说,_pdep_u32_pdep_u64由 GCC 的 immintrin.h 定义无论命令行选项如何(这将定义 __BMI2__ CPP 宏)。也许原因是这个用例:具有目标属性的单个函数,或具有编译指示的函数 block 。

_pdep_u64版本需要 BMI2 + x86-64,而 32 位版本仅需要 BMI2(即也可用于 32 位模式)。 pdep is a scalar integer instruction因此 64 位操作数大小需要 64 位模式。

<小时/>

Aside: MSVC compiled the instrinsic without fuss

是的,MSVC 和 GCC/clang 对于内在函数有不同的理念。 MSVC 允许您使用任何内容,前提是您将进行运行时调度以确保执行永远不会到达 native 不支持的内部函数。

这可能与 MSVC 本质上根本不优化内在函数有关,通常甚至不通过它们进行简单的常量传播。我猜想,因为它试图确保当 C++ 抽象机中未达到内在函数时,相应的 asm 指令永远不会执行,并采取缓慢而安全的方法。

but the program crashed (I suppose _pdep_u32 is not supported by my cpu as suggested in the linked question).

是的,并非所有 CPU 都有 BMI2。您对 GCC 所做的任何事情都不会改变这一点。

关于c - 如何从预处理器启用内在函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59590790/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com