为什么不编译器优化平凡包装函数指针?

问题描述 投票:4回答:2

考虑下面的代码片段

#include <vector>
#include <cstdlib>

void __attribute__ ((noinline)) calculate1(double& a, int x) { a += x; };
void __attribute__ ((noinline)) calculate2(double& a, int x) { a *= x; };
void wrapper1(double& a, int x) { calculate1(a, x); } 
void wrapper2(double& a, int x) { calculate2(a, x); } 

typedef void (*Func)(double&, int);

int main()
{
    std::vector<std::pair<double, Func>> pairs = {
        std::make_pair(0, (rand() % 2 ? &wrapper1 : &wrapper2)),
        std::make_pair(0, (rand() % 2 ? &wrapper1 : &wrapper2)),
    };

    for (auto& [a, wrapper] : pairs)
        (*wrapper)(a, 5);

    return pairs[0].first + pairs[1].first;
}

随着-O3优化最新的gcc和铛版本不优化指针包装的指针到基本功能。见组件here在管线22:

mov     ebp, OFFSET FLAT:wrapper2(double&, int)   # tmp118,

这导致后来在call + jmp,而不是仅仅call有编译器把一个指针代替calculate1

请注意,我特别强调了无内联calculate功能说明;这样做没有在非最优化,其中编译器将产生两个相同的功能的另一香味noinline结果由指针被调用(所以仍然不会优化,只是以不同的方式)。

我缺少的是在这里吗?有没有什么办法来引导的正确功能手动堵漏(不含包装)编译器短?

编辑1.继在意见建议,here is a disassembly所有功能声明为static,具有完全相同的结果(call + jmp代替call)。

编辑同一模式的2非常简单的例子:

#include <vector>
#include <cstdlib>

typedef void (*Func)(double&, int);

static void __attribute__ ((noinline)) calculate(double& a, int x) { a += x; };
static void wrapper(double& a, int x) { calculate(a, x); } 

int main() {
    double a = 5.0;
    Func f;
    if (rand() % 2)
        f = &wrapper; // f = &calculate;
    else
        f = &wrapper;
    f(a, 0); 
    return 0;
}

的gcc 8.2通过投掷指针包装器离开并直接在其位置(&calculate)存储https://gcc.godbolt.org/z/nMIBeo成功优化此代码。然而转产为根据注释(即手动执行相同的优化的一部分)打破了毫无意义的jmp魔术和结果。

c++ gcc assembly compiler-optimization
2个回答
4
投票

你似乎在暗示&calculate1应存放在向量代替&wrapper1。一般来说,这是不可能的:以后的代码可能会尝试对&calculate1所存储的指针比较和必须比较假。

我进一步假设你的建议是,编译器可能会尝试做一些静态分析和判断函数指针的矢量值不会比与其它函数指针平等,事实上,没有任何其它操作上的矢量进行元件将产生可观察到的行为的变化;因此,在这个确切的计划也可能改为存储&calculate1

通常情况下,回答“为什么编译器不能执行一些特定的优化”是,没有人设想和实施这一想法。另一个常见原因是所涉及的静态分析,在一般情况下,相当困难,并可能导致编译放缓与在分析不能保证成功真正的程序没有任何好处。


0
投票

你是赚了很多假设,在这里。首先你的语法。二是编译器在旁观者的眼中完美的,赶上了一切。现实情况是,它很容易找到并手工优化编译器的输出,它并不难写的小功能,绊倒了一个编译器,你很合拍或写一个体面的大小应用将会有地方可以用手调。这是所有已知的和预期的。于是舆论来在我的机器上我的等等比等等更快,因此应该让这些指令来代替。

GCC不是性能有很大的编译器,在某些目标已经获得一批重大转速差。这是在做什么的还不错,比相当不错的好,它涉及了一些预处理器/语言有一个共同的中间和一些后端。有些后端得到更好的优化应用前向后人只是挂在的行列。回来时,我用来照顾有一些其他的编译器可能产生的代码,可以轻松地跑赢大市的gcc。

这些人大多是付费的编译器。不止一个人会支付的口袋里,二手车的价格,有时每年反复出现。

有些事情,GCC可以优化简直太神奇和时间,它完全错误的方向前进。同去的叮当声,他们经常做类似的输出类似的工作,有的时候一些印象深刻的事情有时只是去,误入歧途。我现在发现更有趣的操作优化,使其做好或坏事评定者不是担心你为什么不就做什么,我“觉得”它应该在特定的场合进行。如果我需要更快的代码我把编译后的输出和手工修复它并使用它作为一个组件的功能。完成。

你得到你所支付的与海湾合作委员会的东西,如果你看看在它的肠子深,你会发现它几乎是用胶带和电线百灵一起举行(LLVM正在迎头赶上)。但是,对于一个免费工具,它只是一个了不起的工作,它的用途很广泛,你可以得到免费的技术支持几乎任何地方。我们很遗憾的是早已进入一个时间,人们认为,因为GCC在解释这是语言是如何定义的某种方式的语言和可悲的是,是不是真正的远程。但是,这么多的人不要尝试其他的编译器来找出实现定义的真正含义。

最后也是最重要的,它的开源,如果你想“修理”的优化......然后就去做......记住,修复为自己或张贴或尝试上游推动它。

© www.soinside.com 2019 - 2024. All rights reserved.