考虑到这一功能,

float mulHalf(float x) {
    return x * 0.5f;
}

以下函数使用normal个输入/输出生成相同的结果.

float mulHalf_opt(float x) {
    __m128i e = _mm_set1_epi32(-1 << 23);
    __asm__ ("paddd\t%0, %1" : "+x"(x) : "xm"(e));
    return x;
}

这是-O3 -ffast-math的程序集输出.

mulHalf:
        mulss   xmm0, DWORD PTR .LC0[rip]
        ret

mulHalf_opt:
        paddd   xmm0, XMMWORD PTR .LC1[rip]
        ret

-ffast-math启用-ffinite-math-only,"假定参数和结果不是NaN或+-Infs"[1].

因此,如果mulHalf的编译输出在-ffast-math的容差范围内生成更快的代码,则最好使用paddd-ffast-math.

我从Intel Intrinsics Guide中得到了以下表格.

(MULSS)
Architecture    Latency Throughput (CPI)
Skylake         4       0.5
Broadwell       3       0.5
Haswell         5       0.5
Ivy Bridge      5       1

(PADDD)
Architecture    Latency Throughput (CPI)
Skylake         1       0.33
Broadwell       1       0.5
Haswell         1       0.5
Ivy Bridge      1       0.5

显然,paddd是一个更快的指令.然后我想可能是因为整数和浮点单元之间的旁路延迟.

This answer显示来自Agner Fog的表格.

Processor                       Bypass delay, clock cycles 
  Intel Core 2 and earlier        1 
  Intel Nehalem                   2 
  Intel Sandy Bridge and later    0-1 
  Intel Atom                      0 
  AMD                             2 
  VIA Nano                        2-3 

看到这一点,paddd似乎仍然是赢家,尤其是在Sandy Bridge之后的CPU上,但为最近的CPU指定-march只是将mulss改为vmulss,这具有类似的延迟/吞吐量.

为什么GCC和Clang不将乘法优化为2^n,浮点为paddd,甚至-ffast-math

推荐答案

This fails for an input of 100-ffast-math不排除.(尽管从技术上讲,这是一个次正常值的特例,恰好也有一个零尾数.).

整数减法将换行到一个"全一"指数字段,并翻转符号位,因此得到0.0f * 0.5f产生-Inf,这是不可接受的.

除此之外,是的,我认为这会起作用,并且在CPU(Nehalem除外)上的旁路延迟与ALU延迟中为自己付费,即使在其他FP指令之间使用.

0.0的行为是一种阻碍.除此之外,对于其他输入,底流行为远不如FP乘法,例如,即使设置了FTZ(输出刷新为零),也会产生低于正常值的情况.使用DAZ集(非规范化为零)读取它的代码仍然可以正确处理它,但对于具有最小规范化指数(编码为1)和非零尾数的数字,FP位模式也可能是错误的.e、 g.将规范化数字乘以0.5f,可以得到0x00000001的位模式.

即使不是为了0.0f名表演停止者,这种怪异也可能超过GCC愿意对人们施加的影响.因此,即使GCC可以证明为非零,我也不会期望它,除非它也可以证明远离FLT\u MIN.这可能非常罕见,不值得寻找.

You can certainly do it manually when you know it's safe, although much more convenient with SIMD intrinsics.我认为标量类型双关的asm相当糟糕,可能是整数sub的2x movd,而不是在只需要低标量FP元素时将其保留在XMM for paddd中.

C++相关问答推荐

变量的const视图是否定义良好?

ISO_C_BINDING,从Fortran调用C

C如何显示字符串数组中的第一个字母

括号中的堆栈实现错误问题

使用GOTO从多个嵌套循环C继续

VS代码';S C/C++扩展称C23真关键字和假关键字未定义

Square不与Raylib一起移动

用C语言计算文本文件中的整数个数

添加函数会 destruct 嵌入式C代码(无IDE)

我在反转双向链表时遇到问题

循环中的静态变量与块中的变量和循环

pthread_create的用法

OSDev--双缓冲重启系统

如何使这个While循环在新行上结束

如何组合两个宏来初始化C语言中的字符串数组?

如何在C中处理流水线中的a、n命令?

如何打印循环调度问题的时间表

UpDown控制与预期相反

多行表达式:C 编译器如何处理换行符?

C 中类型说明符的顺序重要吗?