我可以告诉我的编译器忽略语句或函数的副作用吗?

问题描述

假设我的代码具有以下功能

inline int foo() {
    bar();
    int y = baz();
    return y;
}

并假设bar()baz()有副作用。

如果我写:

int z = foo();
printf("z is %d\n",z);

那么显然bar()baz()都必须执行。但是如果我写:

foo();

并且不使用返回值,所以调用baz()的唯一原因是它的副作用。

有没有办法告诉我的编译器(对于GCC,clang,MSVC,Intel中的任何一个)类似“您可能会忽略baz()的副作用,以决定是否对其进行优化” ?还是“您可能会忽略此说明的副作用,以决定是否对其进行优化”?

此问题用C和C ++表示,但可能与各种过程语言有关。让我们使其成为C ++,因为这是我现在最常使用的。

注意:

  • bar()baz()不在我的控制之下。
  • foo()的签名不得更改。当然,如果我愿意的话,我可以使用一些代理并懒洋洋地申请baz();或者在不使用y的情况下编写其他函数。那不是我要的。

解决方法

作为对标准C和C ++的扩展,gcc和clang支持pure function attribute,它通知编译器该函数没有副作用,可以删除不需要的调用。如果使用此属性声明一个函数,即使您在说谎,编译器也会相信您。

#include <iostream>

static int foo() __attribute__((pure));

static int foo() {
  std::cout << "I am a side effect!" << std::endl;
  return 17;
}

int main() {
  foo();
  return 0;
}

This prints no output.请注意,clang会警告您忽略了pure函数的返回值。

(特别是,当您在C中执行类似操作时,gcc optimizes out the call with -O0,但leaves it in with -O2!)

该函数的定义不需要在此范围内起作用,并且即使先前的声明已在范围内,也可以使用此属性重新声明一个函数。因此,它也可以用于库函数。 Example.

当然,对编译器撒谎总是需要您自担风险,并且可以想象,这可能会带来潜在的不良影响,而我从未想到过。如果您愿意仔细检查生成的代码以确保它确实在执行您想要的操作,那么这可能是一个有用的技巧,但是我对此不太信任。

,

在很多情况下,函数的副作用仅在最终使用返回值时才有意义,但是据我所知,C和C ++都没有任何表示它们的方法。 / p>

作为一个常见示例,拥有执行一些计算并返回结果的例程可能会很有用,但是如果(例如由于溢出)它们无法返回不是明显错误的结果,则会引发陷阱。取决于使用函数的方式,使其陷入陷阱可能比返回一个可观察到的错误答案无穷无尽,但使其行为与以某种方式产生正确答案却无法区分的方式可能不那么可取。如果一个程序执行了十二个计算,但只使用了十个,那么完全跳过未使用的计算可能比生成代码以查看其中是否会发生溢出更为有效。

也许有用的是构造一种结构,以测试编译器是否可以保证特定左值的值永远不会明显影响程序行为;一个实现总是被允许说它不能实现,但是在编译器可以提供这种保证的情况下,它将允许适当编写的代码这样说,例如“如果此函数将要返回的x的值永远不会被使用,请不要费心计算它;否则,请检查是否会发生溢出和陷阱;如果使用了返回值,则在那里不会溢出,请计算该函数的值。“

但是,我不知道有任何提供这种功能的C实现。