【问题标题】:Optimize loop around a get and function call using returned value?使用返回值优化获取和函数调用的循环?
【发布时间】:2011-04-28 08:45:19
【问题描述】:

这是一个从缓冲源获取数据并将其发送以进行处理的片段。如果队列为空,get() 返回 null,process 方法很乐意接受 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 行版本。

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

【问题讨论】:

  • “啊,动力!?”很高兴你问。这是一个面试问题,在进入 2 行表格后,我说我认为我们已经完成了。与我交谈的人非常惊讶,并坚定地说我错过了一些东西。很明显的事情,她不会告诉我那是什么,我应该考虑一下。所以我有。 (很明显,我没有给人留下深刻的印象并且不再是该工作的候选人......)这就是为什么我想知道单行版本 - 以及为什么我制作和测量测试用例。优化解决了它。也许她的意思是我将界面更改为传递引用。

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


【解决方案1】:

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

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

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

结合可能的 queue_class::get() 内联,您的问题似乎完全没有意义

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 这也是我的看法。我很高兴看到其他人这么看!
    【解决方案3】:

    这段代码的问题不在于你做了什么,而在于它必须旋转 - 浪费 CPU 周期,你的计算机执行的某些其他任务可能已经使用了 - 即使没有工作要做。

    如果有很多程序采取这种态度(他们是计算机之王,并且会占用整个 CPU),那么一切都会慢下来。让您的代码像这样工作是一个非常激进的决定。

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

    【讨论】:

    • +1 完全正确。我打算建议加速这个循环的明显方法是并行化它。当然,我很确定这不是原始问题的意思。
    • 约瑟夫,你显然是正确的,我没有想到这一点。陈述的问题是这个循环的性能是全部和全部,但值得注意的是计算的浪费。解释就这么长了,我不想再补充了。
    【解决方案4】:

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

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

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

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

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

    【讨论】:

    • 如果需要处理积压的数据,这可能(如果你关心的话,总是基准测试)有助于提高吞吐量,但是当只有第一个 get() 返回数据时会增加延迟。 ..
    • 是的,总是基准测试 :) 是的:它优化的是吞吐量而不是延迟。
    • 创造性思维+1;太糟糕了,在这种情况下它可能是学术性的:)
    • 这可能会得到回报——在两段不同的代码之间进行 bang-bang 可能比对一个代码段进行多次调用和对另一段代码进行多次调用要慢。我也意识到,一个线程读取 g.get() 和一个线程 processing() 的多线程,每个线程都有一个变量,很可能比顺序的单线程运行得更快。然后是 FIFO 缓冲队列...最大流量,而不是最短传输时间,是既定目标。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-15
    • 1970-01-01
    • 2011-11-06
    • 1970-01-01
    • 1970-01-01
    • 2012-06-14
    • 1970-01-01
    相关资源
    最近更新 更多