使用返回值优化get和函数调用的循环?

时间:2011-04-28 08:45:19

标签: c++ pass-by-reference pass-by-value temporary

这是一个从缓冲源获取数据并将其发送以进行处理的片段。如果队列为空,则get()返回null,并且进程方法很乐意采用null并且不执行任何操作。 编写此代码的最佳方法是什么?

something a; // any legal C++ return type...
aQueueOfSomethings g;

while (true) { 
    a=g.get();
    process(a);
}

没有办法预测通过get()到达的值,它们就是它们,并且它们需要被出列并尽快传递给process()。

我没有看到很多浪费的努力 - 如果我跳过名为'a'的显式局部变量并使循环成为一个班轮:

    process(g.get());

g.get()的隐式返回值仍然会分配空间,可能涉及构造函数调用等,等等。

如果返回的东西有任何大小或复杂性,最好有一个指向它的指针而不是它的副本,并传递指针而不是按值复制...所以我宁愿有

something *a;

    g.get(a);
    process(a);

而不是

 something a;

    a=g.get();
    process(a);

我用c ++编写了一个测试用例,尝试了两行和一行版本,循环100,000,000次。

如果a是一个有4个整数和2个浮点数的对象,并且process()方法全部触及它们,那么这两行解决方案实际上更快!如果a对象是单个int,则单行版本更快。如果对象很复杂但process()方法只触及一个值,则单行版本更快。

对我来说最有趣的是,使用g ++编译器,Mac OS X 10.5.8,-O第一级优化开关可以实现相同,更快的操作,同时具有1和2行版本。

除了让编译器优化,两个方法的单行和没有显式的中间变量,并通过引用传递以避免复制,是否有什么通常会使它运行得更快?我觉得我错过了一些明显的东西。

5 个答案:

答案 0 :(得分:3)

我认为这是无用优化的最佳案例

(你正在采取缓冲想要对其进行位优化的东西吗?)

此外,编译器将两种方式编译为完全相同的代码,并且(在大多数情况下)完全有权进行返回值优化尾部调用优化。

结合可能内联的queue_class :: get()你的问题似乎完全是MOOT

答案 1 :(得分:3)

我相信你正试图在自己的工作中击败编译器。

您是否遇到过性能问题?如果没有,您可能会专注于生成一个可以维护的可读代码(您可以使用它),而不是诉诸于过早优化并使代码混乱的奇怪优化。

答案 2 :(得分:2)

此代码的问题不在于您所做的事情,而在于它必须旋转 - 浪费CPU 周期,而计算机正在执行的某些其他任务可能会使用 - 即使没有工作要做。

如果有很多程序采取这种态度(他们是计算机的王者,并且会占用整个CPU),那么一切都会慢慢变成绝对的爬行。让代码像这样工作是一个非常激烈的决定。

如果可能,请更改整个模型,以便在有更多可用数据时获得某种回调/信号/事件

答案 3 :(得分:1)

你应该让编译器优化你是对的,但是如果你知道这样做是安全的:

while (true) { 
    a=g.get();
    b=g.get();
    c=g.get();
    d=g.get();
    process(a);
    process(b);
    process(c);
    process(d);
}

那么它可能会让事情变得更快。

或者,甚至更极端,准备好返回类型(或指针)的整个数组,然后循环处理它们。如果process()和get()都使用了大量代码,那么这样做可能意味着所有代码都可以保留在立即缓存中,而不是每次调用函数时都从另一个缓存中获取。

编译器无法进行此优化,因为它可能不知道重新排序函数调用是安全的。

答案 4 :(得分:-1)

浪费精力 - 当你说:

时,你正在对operator =()进行不必要的调用
a=g.get();

此外,某些东西可能不是默认构造的,在这种情况下你根本不能写第一个表格。