有没有一种方法可以让编译器优化掉对外部函数的多次调用?

5

我正在构建一个类似于这样的C++接口,用于连接C库:

extern "C" {

typedef struct CFooStruct *CFoo;

int CFoo_getLength(CFoo);

// other functions

}

目前我有

class MyFoo {
    CFoo foo;
    int len;
public:
    MyFoo(CFoo foo) : foo(foo), len(CFoo_getLength(foo)) { }

    int length() const { return len; } // inline function

    // other functions
};

构造函数中检索并缓存长度,因此可以在紧密循环中反复调用 MyFoo::length(),而不会有性能损失。
当直接使用 C 接口时,如果需要,需要手动检索长度一次,然后重复使用。 如果不需要 CFoo 的长度,则永远不会调用 CFoo_getLength()
C++ 接口旨在更简单易用,并让用户只需使用 length(),而无需考虑性能。 上述实现的缺点是它在创建每个 MyFoo 对象时都始终调用 CFoo_getLength(),而不管它是否实际上将在程序中使用。
即使 MyFoo 的所有成员函数都是内联的,我认为编译器也无法优化掉对 CFoo_getLength() 的调用,因为它不知道这个函数没有副作用。
问题:有没有办法实现仅在程序实际使用长度时才调用 CFoo_getLength()?(并且它不会为 MyFoo 对象调用多次)有没有办法允许编译器优化掉 CFoo_getLength() 调用(如果它聪明到足以推断出它不需要)?
一种方法是在类中设置一个布尔标志,指示长度是否已经被检索:
class MyFoo2 {
    CFoo foo;

    bool lenKnown = false;
    int len;
public:
    MyFoo2(CFoo foo) : foo(foo) { }

    int length() {
        if (!lenKnown) {
            len = CFoo_getLength(foo);
            lenKnown = true;
        }
        return len;
    }
};

但这是一种运行时解决方案,它使MyFoo变得更大,并在MyFoo::length()内导致额外的计算。我想知道是否有一种编译时解决方案。


@Ryan 它也适用于clang 5.0。你会发表一个答案吗? - Szabolcs
1
@StoryTeller:哦,好的。那么允许从其他地方链接实现的正确方法是什么?(这是否意味着当我编译包含头文件的单个.cpp文件到.o时是危险的?) - Ry-
1
@Ryan - 对于在线编译器,我不好说。目前我也没有GCC的访问权限来进行验证。我并不是要打压兴致,只是想指出OP需要进行更加严格的测试,而不仅仅是在godbold上测试一下。 - StoryTeller - Unslander Monica
1
@Ryan - 关于你的括号问题。这并不危险。ODR方面适用于整个程序,而不是单个翻译单元。因此,只要你在最后将所有内容链接起来,就可以放心使用了。 - StoryTeller - Unslander Monica
1
@Szabolcs - 在在线编译器中是不行的。我的整个观点是,它在godbolt(存在ODR违规)中可行并不表明它是否应该在一般情况下工作。但如果在您自己的项目中可以工作,那就很好了 :) - StoryTeller - Unslander Monica
显示剩余15条评论
1个回答

3
您可以使用 pure 函数属性来将 CFoo_getLength 标记为纯函数:
__attribute__ ((pure))
int CFoo_getLength(CFoo);

正如你所发现的,令我这个对C++不熟悉的人感到惊讶的是,它允许gcc和clang优化你的原始代码。不错!


我正在阅读文档,看起来适当的属性实际上是pure(而不是const)。文档中说:“请注意,具有指针参数并检查所指数据的函数不能声明为const。” pure没有这样的限制。 - Szabolcs
@Szabolcs:哎呀,我记错了。谢谢。 - Ry-

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接