gcc与ICC中最快的复杂划分

gcc与ICC中最快的复杂划分,gcc,optimization,icc,Gcc,Optimization,Icc,考虑以下简单代码: #include <complex.h> complex double f(complex double x, complex double y) { return x/y; } 这很有道理,也很容易理解。但是,“英特尔C编译器”提供: f: fld1 #3.12 vmovsd QWORD PTR [-24+rsp],

考虑以下简单代码:

#include <complex.h>
complex double f(complex double x, complex double y) {
  return x/y;
}
这很有道理,也很容易理解。但是,“英特尔C编译器”提供:

f:
        fld1                                                    #3.12
        vmovsd    QWORD PTR [-24+rsp], xmm2                     #3.12
        fld       QWORD PTR [-24+rsp]                           #3.12
        vmovsd    QWORD PTR [-24+rsp], xmm3                     #3.12
        fld       st(0)                                         #3.12
        fmul      st, st(1)                                     #3.12
        fld       QWORD PTR [-24+rsp]                           #3.12
        fld       st(0)                                         #3.12
        fmul      st, st(1)                                     #3.12
        vmovsd    QWORD PTR [-24+rsp], xmm0                     #3.12
        faddp     st(2), st                                     #3.12
        fxch      st(1)                                         #3.12
        fdivp     st(3), st                                     #3.12
        fld       QWORD PTR [-24+rsp]                           #3.12
        vmovsd    QWORD PTR [-24+rsp], xmm1                     #3.12
        fld       st(0)                                         #3.12
        fmul      st, st(3)                                     #3.12
        fxch      st(1)                                         #3.12
        fmul      st, st(2)                                     #3.12
        fld       QWORD PTR [-24+rsp]                           #3.12
        fld       st(0)                                         #3.12
        fmulp     st(4), st                                     #3.12
        fxch      st(3)                                         #3.12
        faddp     st(2), st                                     #3.12
        fxch      st(1)                                         #3.12
        fmul      st, st(4)                                     #3.12
        fstp      QWORD PTR [-16+rsp]                           #3.12
        fxch      st(2)                                         #3.12
        fmulp     st(1), st                                     #3.12
        vmovsd    xmm0, QWORD PTR [-16+rsp]                     #3.12
        fsubrp    st(1), st                                     #3.12
        fmulp     st(1), st                                     #3.12
        fstp      QWORD PTR [-16+rsp]                           #3.12
        vmovsd    xmm1, QWORD PTR [-16+rsp]                     #3.12
        ret 
有人能解释一下它在做什么,以及它是否真的是这样吗 比gcc的方法更快


我自己无法对代码进行基准测试,因为我没有ICC。ICC程序集是使用创建的。

根据问题和一些评论的要求,我运行了一个快速基准测试,以比较GCC和ICC编译器在这段C代码上的性能

硬件设置

用于运行测试的机器采用AMD A8-5550M APU四核处理器,频率为2.1GHz。L1i的缓存大小为16k,L1d为64k,L2为2048K

实验装置

我没有ICC编译器的副本,因此问题中列出的汇编代码直接用于此基准测试。这两个汇编输出是使用NASM汇编程序编译的。为了使ICC版本兼容,需要进行一些小的语法更改,但当然不会以任何方式改变功能或影响性能。编写了一个小型C包装器来调用这两个汇编函数和监视计时

下面是一个版本的代码,类似于用于此简单基准测试的代码:

#include <stdio.h> 
#include <complex.h>
#include <time.h>

extern complex double gcc_f(complex double x, complex double y);
extern complex double icc_f(complex double x, complex double y);

int main() {
    struct timespec stop, start;
    complex double z1 = 1.0654575 + 3.0678788768 * I;
    complex double z2 = 2.225 - 8.0 * I;

    clock_gettime(CLOCK_MONOTONIC_RAW, &start);
    for(int i =0; i < 1000000000; ++i) {
        icc_f(z1, z2);
        // gcc_f(z1, z2);
    }
    clock_gettime(CLOCK_MONOTONIC_RAW, &stop);

    printf("Execution took %luns\n", ((stop.tv_sec - start.tv_sec) * 1000000000 + (stop.tv_nsec - start.tv_nsec)));
    return 0;
}
此替代ICC版本速度明显更快,平均每次执行9.0ns,但仍略落后于GCC版本。然而,这些微小的差异可能与实验设置有关。

添加编译器标志:

-fp-model fast=2

这是ICC中的-ffast math On godbolt的等价物。您可以通过单击警告三角形选项来检查编译器的输出

您不能自己进行基准测试吗?调用函数一百万次,使用高精度计时器测量每次调用,然后取平均值。为什么不问问编译器供应商呢?英特尔很乐意改进他们的编译器。@Olaf您的意思是联系他们,让他们执行基准测试,以报告他们的汇编速度是否比gcc快?我不确定他们是否会回答这个问题。有趣的是,英特尔的代码中只有一个fdivp,这可能是有益的,因为除法是昂贵的。@felipa Done,请参考我的答案-非常感谢你。请您添加用于获取计时的源代码,以便我们可以在硬件上复制它。有趣的是,Ofast ICC代码只使用了一个分区,所以看起来是最快的。@eleanora Done,还为替代ICC版本添加了性能数据。再次感谢。您介意将icc_fz1、z2的NASM源代码也包括在内吗?@eleanora这基本上是您在问题中发布的代码,对NASM的语法做了一些小的修改。只需删除3.12,删除PTR关键字,删除st寄存器st0->st0周围的括号,删除独立的st关键字fmul st,st3->fmul st3。我真的开始脱离我的专业领域,但浮点除法不能在现代CPU上管道化吗?我们可能还应该了解这些代码中涉及的每一条指令的性能,而不仅仅是划分-
f:
        vunpcklpd xmm4, xmm2, xmm3                              #2.54
        vunpcklpd xmm6, xmm0, xmm1                              #2.54
        vunpckhpd xmm5, xmm4, xmm4                              #3.12
        vmulpd    xmm10, xmm4, xmm4                             #3.12
        vmulpd    xmm8, xmm5, xmm6                              #3.12
        vmovddup  xmm9, xmm4                                    #3.12
        vshufpd   xmm7, xmm6, xmm6, 1                           #3.12
        vshufpd   xmm11, xmm10, xmm10, 1                        #3.12
        vfmaddsub213pd xmm9, xmm7, xmm8                         #3.12
        vaddpd    xmm13, xmm10, xmm11                           #3.12
        vshufpd   xmm12, xmm9, xmm9, 1                          #3.12
        vdivpd    xmm0, xmm12, xmm13                            #3.12
        vunpckhpd xmm1, xmm0, xmm0                              #3.12
        ret 
-fp-model fast=2