【问题标题】:Fastest complex division in gcc versus ICCgcc 与 ICC 中最快的复杂除法
【发布时间】:2017-06-19 09:37:30
【问题描述】:

考虑这个简单的代码:

#include <complex.h>
complex double f(complex double x, complex double y) {
  return x/y;
}

在带有 -O3 -march=core-avx2 -ffast-math 的 gcc 7.1 中,您会得到:

f:
        vmulsd  xmm4, xmm1, xmm3
        vmovapd xmm6, xmm0
        vmulsd  xmm5, xmm3, xmm3
        vmulsd  xmm6, xmm6, xmm3
        vfmadd231sd     xmm4, xmm0, xmm2
        vfmadd231sd     xmm5, xmm2, xmm2
        vfmsub132sd     xmm1, xmm6, xmm2
        vdivsd  xmm0, xmm4, xmm5
        vdivsd  xmm1, xmm1, xmm5
        ret

这是有道理的,而且很容易理解。然而,英特尔 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 程序集是使用https://godbolt.org/g/ZXZGy2 创建的。

【问题讨论】:

  • 你不能自己做一个基准测试吗?调用函数一百万次,使用高精度计时器测量每个调用,然后取平均值。
  • 为什么不问问编译器供应商?英特尔很乐意改进他们的编译器。
  • @Olaf 您的意思是联系他们要求他们执行基准测试以报告他们的组装是否比 gcc 快?我不确定他们会回答这个问题。
  • 有趣的是,英特尔的代码中只有一个 fdivp,这可能是有益的,因为分割成本很高。
  • @felipa 完成,请参考我的回答。 ;-)

标签: gcc optimization icc


【解决方案1】:

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

硬件设置

用于运行测试的机器配备 AMD A8-5550M APU 四核处理器,频率为 2.1 GHz。 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;
}

结果

这两个时间都是十亿次处决的平均值。

GCC 版本平均每次执行耗时 8.8ns

ICC 版本平均每次执行耗时 17.3ns

因此,GCC 编译器的性能明显优于 ICC 编译器,至少在上述特定硬件设置方面是这样。在这种情况下,GCC 似乎更巧妙地使用了 AVX 指令集。


附带说明,非常有趣的是,如果您使用 -Ofast 而不是 -O3 进行编译,ICC 版本看起来更类似于 GCC 版本:

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 

此替代 ICC 版本明显更快,平均每次执行 9.0ns,但仍略低于 GCC 版本。然而,如此小的差异可能与实验设置有关。

【讨论】:

  • 非常感谢。您能否添加用于获取时间的来源,以便我们可以在我们的硬件上重现它。有趣的是,Ofast ICC 代码只使用一个分区,所以看起来是最快的。
  • @eleanora Done,还添加了替代 ICC 版本的性能数据。
  • 再次感谢您。您是否介意也包括icc_f(z1, z2) 的 NASM 源代码。
  • @eleanora 这基本上是您在问题中发布的代码,对 NASM 进行了一些小的语法更改。只需删除#3.12,删除PTR 关键字,删除st 寄存器周围的括号(st(0) -> st0)并删除独立的st 关键字(fmul st, st(3) -> fmul st3)。跨度>
  • 我真的开始脱离我的专业领域,但浮点除法不能在现代 CPU 上流水线化吗?我们可能还应该阅读这些代码中涉及的每条指令的性能,而不仅仅是部门。 ;-)
【解决方案2】:

添加编译器标志:

-fp-model fast=2

这是 -ffast-math 的 ICC 等效项(在 Godbolt 上,您可以通过单击警告三角形选项来检查编译器输出)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-19
    • 1970-01-01
    • 2016-02-11
    • 2013-05-17
    • 2019-10-14
    • 1970-01-01
    相关资源
    最近更新 更多