什么编译器命令可以用来使GCC和ICC编译程序的速度一样快?[closed]

rnmwe5a2  于 2023-01-09  发布在  其他
关注(0)|答案(2)|浏览(142)

2天前关闭。
Improve this question
enter image description hereCodeasm diff
我不是很明白这是什么优化,我只知道它真的很快,我已经尝试了手册中的许多命令都没有用。谁能详细解释一下这是什么优化,以及GCC中的哪个命令可以生成与ICC相同或更好的ASM?

d7v8vwbk

d7v8vwbk1#

我不确定是否有一个优化选项可以让GCC做这个优化。如果你不想让你的程序花时间做同样的事情,就不要写重复10万次的循环。
击败基准循环可以使编译器在基准测试中看起来很好,但是AFAIK在实际代码中通常没有用,因为在您想要优化的循环运行之间会发生其他事情。

ICC将您的基准重复循环转换为以下形式,从而击败了它

for (unsigned c = 0; c < arraySize; ++c)
    {
        if (data[c] >= 128)
            for (unsigned i = 0; i < 100000; ++i)
                sum += data[c];
    }

第一步是交换内部循环和外部循环,称为loop interchange。对数组进行一次遍历有利于缓存局部性,并支持进一步优化。
for() if()转换为if() for(){} else for(){}称为loop unswitching,在这种情况下,没有“其他”工作要做;循环中唯一的东西是if()sum+=...,所以它变成仅仅是控制重复加法循环的if。
ICC展开+矢量化sum +=,奇怪的是,它不只是用乘法来做,而是用100000 64位加法运算。ymm 0保存vpbroadcastq中的_mm256_set1_epi64(data[c])
此内部循环只有条件地运行;如果要保存这个循环的6250次迭代,就值得进行分支(只遍历数组一次,每个元素一次分支,而不是100 k)。

..B1.9:                         # Preds ..B1.9 ..B1.8
        add       edx, 16                                       #20.2
        vpaddq    ymm4, ymm4, ymm0                              #26.5
        vpaddq    ymm3, ymm3, ymm0                              #26.5
        vpaddq    ymm2, ymm2, ymm0                              #26.5
        vpaddq    ymm1, ymm1, ymm0                              #26.5
        cmp       edx, 100000                                   #20.2
        jb        ..B1.9        # Prob 99%                      #20.2

每次迭代执行16次加法,每条指令4次,按4展开到单独的累加器中,累加器减为1,然后在循环后进行h求和。展开允许Skylake和以后的版本在每个时钟周期运行3 vpaddq
相比之下,GCC在数组上执行多次传递,在循环内向量化以无分支地执行8次比较:

.L85:
        vmovdqa ymm4, YMMWORD PTR [rax]      # load 8 ints
        add     rax, 32
        vpcmpgtd        ymm0, ymm4, ymm3     # signed-compare them against 128
        vpand   ymm0, ymm0, ymm4             # mask data[c] to 0 or data[c]
        vpmovsxdq       ymm2, xmm0           # sign-extend to 64-bit
        vextracti128    xmm0, ymm0, 0x1
        vpaddq  ymm1, ymm2, ymm1             # and add
        vpmovsxdq       ymm0, xmm0           # sign-extend the high half and add it
        vpaddq  ymm1, ymm0, ymm1             # to the same accumulator
        cmp     rbx, rax
        jne     .L85

这是在一个重复循环内部,该循环在数组上进行多次传递,并且可能在每个时钟周期1个shuffle微操作时出现瓶颈,就像Skylake上每3个时钟周期8个元素一样。
所以它只是像你所期望的那样向量化了内部的if() sum+=data[c],根本没有破坏repeat循环。

ippsafx7

ippsafx72#

编译器生成功能上等同的代码,没有理由假设从一个输入到目标有一个完美的输出。如果两个编译器为相对合适大小的函数/项目生成相同的输出,那么一个编译器是从另一个编译器派生的,合法与否。
一般来说,没有理由假设任何两个编译器生成相同的输出,或者同一个编译器的任何两个版本生成相同的输出。通过命令行选项来放大这一点,这些选项将改变编译器的输出。
一般来说,人们期望一个编译器能为你的代码生成“更好”的代码,这取决于“更好”、“更小”或“在一台特定的计算机、操作系统等上运行得更快”的定义。
gcc是一种通用计算机,它对每个目标都“不错”,但对任何目标都不是很好。一些从头开始设计的针对一个目标/系统的工具可以(但不一定)做得更好。然后可能会有一些欺骗。以一些代码为例,比如可能是故意写得很糟糕的Drystone和Whetstone,那么当X编译器,也许你已经付出(五位数)为或正在评估支付,不产生代码的dhrystone是一样快的免费工具。哦,当然,尝试这个命令行选项为dhrystone.嗯好的,这样做更好(到过那里,gcc自从3.x.x/4.x.x.版本以来一直在变差。我认为原因是多方面的。我认为,部分原因是真正在这一级别工作的人正在死亡,并被没有低水平经验和技能的人所取代。处理器变得越来越复杂,gcc和其他公司有了更多的目标,但是老版本提供的优化失败的数量在增加,同样的源代码和设置的二进制文件的大小也在增加,增加的幅度很大,对于一个规模合适的项目来说,不仅仅是一点点。
这不是一个情况下,我需要得到车轮上的汽车,我有一个工具的选择,我可以用来拧紧螺母,这是相同的结果独立于工具。
没有理由期望任何两个编译器可以生成相同的输出,即使这两个工具都生成汇编语言,并且生成相同的指令和数据序列,也没有理由假设汇编语言本身来自用于该目标的不同汇编语言,不同的标签名称和间距、函数排序以及其他会使diff难以处理的语法。

相关问题