c++ - C++ 中的高效整数下限函数

标签 c++ performance x86-64 processing-efficiency floor

我想定义一个有效的整数下限函数,即从 float 或 double 执行截断到负无穷大的转换。

我们可以假设这些值不会发生整数溢出。到目前为止,我有几个选择

  • 转换为 int;这需要对负值进行特殊处理,因为强制转换趋向于零;

    I= int(F); if (I < 0 && I != F) I--;
    
  • 将 floor 的结果转换为 int;

    int(floor(F));
    
  • 转换为 int 并进行较大的转换以获得正数(这可能会为较大的值返回错误的结果);

    int(F + double(0x7fffffff)) - 0x7fffffff;
    

众所周知,转换为 int 的速度很慢。 if 测试也是如此。地板功能我没有计时,但看到帖子声称它也很慢。

您能在速度、准确性或允许范围方面想出更好的选择吗?它不需要是可移植的。目标是最近的 x86/x64 架构。

最佳答案

Casting to int is notoriously slow.

也许您自 x86-64 以来就一直生活在一 block 岩石下,或者以其他方式错过了在 x86 上这已经有一段时间了。 :)

SSE/SSE2 有一个指令来转换用截断(而不是默认的舍入模式)。 ISA 有效地支持此操作正是因为在实际代码库中使用 C 语义进行转换并不罕见。 x86-64 代码使用 SSE/SSE2 XMM 寄存器进行标量 FP 数学,而不是 x87,因为这个和其他使它更高效的东西。即使是现代 32 位代码也使用 XMM 寄存器进行标量数学运算。

当为 x87 编译时(没有 SSE3 fisttp),编译器过去必须将 x87 舍入模式更改为截断,FP 存储到内存,然后再次更改舍入模式。 (然后从内存中重新加载整数,通常从堆栈上的本地重新加载,如果对它进行进一步的处理。)x87 对此糟糕

是的, 非常慢,例如在 2006 年编写 @Kirjain 答案中的链接时,如果您仍然拥有 32 位 CPU 或正在使用 x86-64 CPU 运行 32 位代码。


不直接支持使用截断或默认(最近)以外的舍入模式进行转换,直到 SSE4.1 roundps/roundpd 你最好的选择是魔术 - the 2006 link 中的数字技巧来自@Kirjain 的回答。

那里有一些不错的技巧,但仅适用于 double -> 32 位整数。如果您有 float,则不太可能值得扩展到 double

或者更常见的是,只需添加一个大数值来触发舍入,然后再次减去它以返回原始范围。这可以用于 float 而无需扩展到 double,但我不确定让 floor 工作有多容易。


无论如何,这里明显的解决方案是 _mm256_floor_ps()_mm256_cvtps_epi32(vroundpsvcvtps2dq )。它的非 AVX 版本可以与 SSE4.1 一起使用。

我不确定我们是否可以做得更好;如果您有一个庞大的数组要处理(并且无法设法将这项工作与其他工作交错),您可以将 MXCSR 舍入模式设置为“towards -Inf”(地板)并简单地使用 vcvtps2dq(使用当前的舍入模式)。然后放回去。但最好缓存阻止您的转换,或者在生成数据时即时执行,可能来自需要将 FP 舍入模式设置为默认最近的其他 FP 计算。

roundps/pd/ss/sd 在 Intel CPU 上是 2 uop,但在 AMD Ryzen 上只有 1 uop(每 128 位 channel )。 cvtps2dq 也是 1 uop。打包的 double->int 转换还包括随机播放。标量 FP->int 转换(复制到整数寄存器)通常还需要额外的 uop。

因此,在某些情况下,魔术数字技巧有可能获胜;如果 _mm256_floor_ps() + cvt 是关键瓶颈的一部分(或者如果您有 double 并且想要 int32,则更有可能)。


@Cássio Renan 的 int foo = floorf(f) 如果使用 gcc -O3 -fno-trapping-math(或 - ffast-math),带有 SSE4.1 或 AVX 的 -march= 东西。 https://godbolt.org/z/ae_KPv

如果您将它与其他未手动矢量化的标量代码一起使用,这可能会很有用。特别是如果您希望编译器自动矢量化整个事情。

关于c++ - C++ 中的高效整数下限函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56417115/

相关文章:

c++ - 给定的 float 位于哪个段?

c++ DLNA 字幕显示实现与白金库

函数内定义的 Python 编译器和常量

assembly - SYSENTER在64位模式下如何使用,IA32_SYSENTER_CS是什么?

c - 如何防止gcc优化破坏rep movsb代码?

c++ - rint 不存在于 Visual Studio 2010 math.h 中,相当于 CUDA rint

c++ - 如何使整个 GridView 行垂直展开

performance - 通过 mov, xor 交换变量的成本

php - NewRelic PHP 代理增加了多少开销?

linux - 内存是从 Linux/x86 上的 mmaping/dev/shm Write-Back (WB) 还是 Non-Cacheable Write-Combining (WC) 返回的?