编译器是否优化按值传递的函数参数?

12 浏览
0 Comments

编译器是否优化按值传递的函数参数?

假设我有一个函数,其中参数是通过值传递而不是const引用传递的。进一步假设函数内部只使用该值,即函数不试图修改它。在这种情况下,编译器能否能够找出它可以通过const引用传递值(出于性能原因)并相应地生成代码?是否有任何编译器可以做到这一点?

0
0 Comments

编译器是否对通过值传递的函数参数进行优化?

通过值传递函数参数是否会被编译器优化,这取决于多个因素。首先,如果被调用的函数被内联,编译器就可以看到是否存在不必要的拷贝操作,并且如果优化级别足够高,拷贝操作可能会被消除。至少GCC编译器可以实现这一优化。

其次,需要考虑传递的参数值的类是否具有拷贝构造函数。如果没有拷贝构造函数,那么通过值传递和通过常量引用传递之间的性能差异可能是可以忽略不计的。

另一方面,如果类具有执行其他操作的拷贝构造函数,那么你所期望的优化可能不会发生,因为编译器无法删除对构造函数的调用,它无法知道构造函数的副作用对你来说是否重要。

如果你能提供参数的类别,或者如果它是自定义类,请描述它具有哪些字段以及是否具有拷贝构造函数,那么或许可以得到更有用的答案。

0
0 Comments

编译器是否会对通过值传递的函数参数进行优化?

只有当函数没有被导出时,编译器才有可能将传递方式从按引用传递转换为按值传递(或反之)。

否则,由于调用约定的原因,函数必须保持按值传递/引用传递的语义。

但是,内联副本当然可以进行优化,即使外部可见的函数版本不能。

从以上内容中可以看出,编译器是否对通过值传递的函数参数进行优化取决于函数是否被导出。如果函数没有被导出,编译器有机会将传递方式从按引用传递转换为按值传递,或者反之。然而,如果函数被导出,由于调用约定的原因,函数必须保持按值传递或引用传递的语义,此时编译器不能对其进行优化。

然而,尽管外部可见的函数版本不能被优化,但内联副本可以进行优化。内联是一种优化技术,它将函数调用处的代码替换为函数体,避免了函数调用的开销。因此,即使外部可见的函数版本不能被优化,通过内联副本仍然可以对函数参数进行优化。

总结起来,编译器是否对通过值传递的函数参数进行优化取决于函数是否被导出。如果函数没有被导出,编译器有机会将传递方式从按引用传递转换为按值传递,或者反之。然而,由于调用约定的原因,被导出的函数必须保持按值传递或引用传递的语义,此时编译器不能对其进行优化。不过,通过内联副本仍然可以对函数参数进行优化。

0
0 Comments

编译器是否优化通过值传递的函数参数?

问题的原因是,如果传递的是一个变量而不是一个临时值,当且仅当复制构造函数执行与程序运行时的可观察行为相关的操作(输入/输出或更改易失性变量)时,编译器不允许优化掉复制操作。除此之外,编译器可以自由地进行任何操作(只需要模拟不进行任何优化的可观察行为)。

只有当参数是rvalue(大多数临时值)时,即使复制构造函数具有可观察的副作用,编译器也可以优化复制到按值传递的参数的过程。

复制是否被排除在as-if规则之外?我认为复制是唯一可以消除的,而不必担心后果。

我认为编译器不需要保证复制构造函数没有“可观察的行为”,因为这也是编译器可以省略从临时值复制的前提。

如果从rvalue复制,复制是被排除在外的。如果从lvalue复制,就不会被排除在外。如果你传递一个rvalue并且编译器内联调用函数的代码,它可以优化掉任何它想要的东西(实际上,我看到GCC将带有三个或四个嵌套调用的数百行汇编代码优化为只有两到三行)。参见C++03规范中的12.8/15。

标准明确规定,即使复制构造函数具有可观察的行为,复制也可以被省略。第15段写道:“当满足某些条件时,即使对象的复制构造函数和/或析构函数具有副作用,实现也可以省略类对象的复制构造。”这就是为什么你可以在复制构造函数中放置打印语句,并在复制时注意到它是否进行了复制。

我不记得具体的“条件”,我以为这是通用的。

现在我有点困惑了。 “这就是为什么你可以在复制构造函数中放置打印语句,并在复制时注意到它是否进行了复制。”似乎直接与“如果复制构造函数执行与程序运行时可观察到的任何操作相关的操作,编译器不允许优化掉复制”相矛盾,似乎支持我的说法后者是不正确的。

如果源是rvalue,编译器是允许这样做的。所以如果你执行void f(Foo); int main() { f(Foo(1)); }你可以通过打印输出来确定编译器是否复制。这就是我在答案中最后一句话的意思。对于lvalue,它将始终进行复制。(当然,返回值优化是另一回事。我的答案不涉及它们,只涉及参数传递,不涉及返回值)。

请注意,所有这些仅适用于如果传递的是变量而不是临时值 🙂 变量不是rvalue。

对于回答和解释为什么可以通过简单的打印语句找出复制省略的评论,我给予+1。

12.8/15没有说,如果原始对象或副本将不再使用,复制可以被省略吗?void foo(X); int main() { X x; foo(x); /*不复制,因为x不再使用*/ }(这也意味着你不能仅仅为了调用复制构造函数而通过值传递某个东西?:))

我没有看到它适用于参数传递。这只适用于返回:X f() { X x; return x; } /*可以省略*/。请注意,如果复制构造函数没有副作用,它总是可以省略,也适用于你的示例(通常情况下都是as-if的情况)。

0