我在编译器资源管理器中摆弄,我发现传递给 std::min 的参数顺序改变了发出的程序集。
这是 Godbolt Compiler Explorer 上的示例
double std_min_xy(double x, double y) {
return std::min(x, y);
}
double std_min_yx(double x, double y) {
return std::min(y, x);
}
Run Code Online (Sandbox Code Playgroud)
这被编译(例如,在 clang 9.0.0 上使用 -O3):
std_min_xy(double, double): # @std_min_xy(double, double)
minsd xmm1, xmm0
movapd xmm0, xmm1
ret
std_min_yx(double, double): # @std_min_yx(double, double)
minsd xmm0, xmm1
ret
Run Code Online (Sandbox Code Playgroud)
如果我将 std::min 更改为老式三元运算符,这种情况仍然存在。它也适用于我尝试过的所有现代编译器(clang、gcc、icc)。
底层指令是minsd. 阅读文档,第一个参数minsd也是答案的目的地。显然 xmm0 是我的函数应该放置其返回值的地方,所以如果 xmm0 用作第一个参数,则movapd不需要。但是如果 xmm0 是第二个参数,那么它必须movapd xmm0, xmm1将值放入 xmm0。(编者注:是的,x86-64 System V在 …
我在 Fortran 和 C++ 中分别实现了一个函数:
#include <math.h>
void dbl_sqrt_c(double *x, double *y){
*y = sqrt(*x - 1.0);
return;
}
Run Code Online (Sandbox Code Playgroud)
pure subroutine my_dbl_sqrt(x,y) bind(c, name="dbl_sqrt_fort")
USE, INTRINSIC :: ISO_C_BINDING
implicit none
real(kind=c_double), intent(in) :: x
real(kind=c_double), intent(out) :: y
y = sqrt(x - 1d0)
end subroutine my_dbl_sqrt
Run Code Online (Sandbox Code Playgroud)
我在编译器资源管理器中比较了它们:
Fortran:https : //godbolt.org/z/froz4rx97
C++:https : //godbolt.org/z/45aex99Yz
我阅读汇编程序的方式,它们基本上做相同的事情,但是 C++ 检查 sqrt 的参数是否为负,而 Fortran 则没有。我使用 googles 基准比较了它们的性能,但它们非常匹配:
--------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------
bm_dbl_c/8 2.07 ns 2.07 ns 335965892
bm_dbl_fort/8 2.06 ns 2.06 …Run Code Online (Sandbox Code Playgroud) 考虑使用以下 float 循环,使用 -O3 -mavx2 -mfma 编译
for (auto i = 0; i < a.size(); ++i) {
a[i] = (b[i] > c[i]) ? (b[i] * c[i]) : 0;
}
Run Code Online (Sandbox Code Playgroud)
Clang 在矢量化方面做得非常出色。它使用 256 位 ymm 寄存器,并了解 vblendps/vandps 之间的差异,以获得尽可能最佳的性能。
.LBB0_7:
vcmpltps ymm2, ymm1, ymm0
vmulps ymm0, ymm0, ymm1
vandps ymm0, ymm2, ymm0
Run Code Online (Sandbox Code Playgroud)
然而,海湾合作委员会的情况要糟糕得多。由于某种原因,它并没有比 SSE 128 位向量更好(-mprefer-vector-width=256 不会改变任何东西)。
.L6:
vcomiss xmm0, xmm1
vmulss xmm0, xmm0, xmm1
vmovss DWORD PTR [rcx+rax*4], xmm0
Run Code Online (Sandbox Code Playgroud)
如果将其替换为普通数组(如指南中所示),gcc 会将其矢量化为 AVX ymm。
int a[256], b[256], c[256];
auto foo …Run Code Online (Sandbox Code Playgroud) 我对这篇文章感到兴奋:/sf/answers/4037224201/,我考虑使用-fno-math-errno. 但我想确保我不会损害我正在开发的软件的行为。
因此,我检查了(相当大的)代码库以查看errno正在使用的位置,并且我想确定这些用法是否会干扰-fno-math-errno. 但如何做到这一点呢?文档说:
-fno-数学错误号
在调用使用单条指令执行的数学函数(例如 sqrt...)后,请勿设置 errno
但我如何知道单条指令执行了哪些数学函数呢?这有记录在某处吗?在哪里?
似乎我使用的代码库errno特别依赖于调用strtol和使用流时。我猜这strtol不是用一条指令执行的。它是否被认为是一个数学函数?我怎样才能确定?
从gcc的文档来看
如果控制流程到达该点
__builtin_unreachable,则程序未定义.
我认为__builtin_unreachable可以用各种创造性的方式暗示优化器.所以我做了一个小实验
void stdswap(int& x, int& y)
{
std::swap(x, y);
}
void brswap(int& x, int& y)
{
if(&x == &y)
__builtin_unreachable();
x ^= y;
y ^= x;
x ^= y;
}
void rswap(int& __restrict x, int& __restrict y)
{
x ^= y;
y ^= x;
x ^= y;
}
Run Code Online (Sandbox Code Playgroud)
被编译为(g ++ -O2)
stdswap(int&, int&):
mov eax, DWORD PTR [rdi]
mov edx, DWORD PTR [rsi]
mov DWORD PTR [rdi], edx
mov DWORD PTR [rsi], eax …Run Code Online (Sandbox Code Playgroud) Windows中sqrt()函数的域错误未将errno设置为EDOM,在Linux上正确显示,但在Windows上失败(使用GCC 7.4)...
#include <stdio.h>
#include <errno.h>
#include <math.h>
int main () {
double val;
errno = 0;
val = sqrt(-10);
if(errno == EDOM) {
printf("Invalid value \n");
} else {
printf("Valid value\n");
}
errno = 0;
val = sqrt(10);
if(errno == EDOM) {
printf("Invalid value\n");
} else {
printf("Valid value\n");
}
return(0);
}
Run Code Online (Sandbox Code Playgroud)
预期结果:无效值有效值实际结果:有效值有效值
I would like to make some vector computation faster, and I believe that SIMD instructions for float comparison and manipulation could help, here is the operation:
void func(const double* left, const double* right, double* res, const size_t size, const double th, const double drop) {
for (size_t i = 0; i < size; ++i) {
res[i] = right[i] >= th ? left[i] : (left[i] - drop) ;
}
}
Run Code Online (Sandbox Code Playgroud)
Mainly, it drops the left value by drop in case right …
以下 3 行使用"gcc -Ofast -march=skylake"给出不精确的结果:
int32_t i = -5;
const double sqr_N_min_1 = (double)i * i;
1. - ((double)i * i) / sqr_N_min_1
Run Code Online (Sandbox Code Playgroud)
显然,第三行中的sqr_N_min_1gets25.和(-5 * -5) / 25应该变为 ,1.因此第三行的整体结果正好是0.。事实上,这对于编译器选项"gcc -O3 -march=skylake"是正确的。
但是使用“-Ofast”,最后一行产生-2.081668e-17而不是0.和i除了-5(例如6或7)之外的其他非常小的正或负随机偏差0.。我的问题是:这种不精确的根源究竟在哪里?
为了调查这个,我用 C 写了一个小测试程序:
#include <stdint.h> /* int32_t */
#include <stdio.h>
#define MAX_SIZE 10
double W[MAX_SIZE];
int main( …Run Code Online (Sandbox Code Playgroud)