2

哪个最有效:使用 null 对象,或 nullptr 上的分支。C++ 中的示例:

void (*callback)() = [](){}; // Could be a class member

void doDoStuff()
    {
    // Some code
    callback();  // Always OK. Defaults to nop
    // More code
    }

对比

void (*callback)() = nullptr; // Could be a class member

void doDoStuff()
    {
    // Some code
    if(callback != nullptr)  // Check if we should do something or not
        {callback();}
    // More code
    }

假设编译器无法内联它,null 对象将始终执行间接函数调用。使用nullptr,总会​​做分支,如果有事要做,也会做间接函数调用。

用指向抽象基类的指针替换回调会影响决策吗?

callback设置为 nullptr 以外的东西的可能性如何。我猜如果callback最有可能是nullptr,那么使用附加分支会更快,对吧?

4

3 回答 3

3

这些问题都不能先验地回答。它们将取决于无数因素,包括但不限于硬件的细节、调用时缓存的细节、围绕此类调用的各种代码(可能隐藏任何引入的延迟)、编译器的内联/去虚拟化能力(这又取决于所有代码的具体细节)等等。

仅当您拥有要优化的确切代码,并且确定所讨论的代码是值得进行此类优化的性能问题时,才应尝试此类微优化。

于 2019-09-30T17:08:22.790 回答
2

您必须测试代码并找出

如果不对其进行测试,根本无法确定此代码是更快还是更慢 - 即使测试与您的构建具有相同的编译器和目标,该测试的结果也可能不适用于您的特定应用程序或环境。

编译器非常擅长优化代码,尤其是在半可预测的行为方面,并且 CPU 通常使用分支预测或其他类似技术构建,以降低必须分支代码的成本。因此,答案完全取决于代码的特定用例、代码的目标设备以及它的运行条件。

于 2019-09-30T17:07:34.500 回答
2

哪个最有效:使用 null 对象,或 nullptr 上的分支。

假设给定的代码生成解决方案,它将取决于您的目标。而且,如果您不这么认为,它还取决于您的代码、编译器和可能的月相。

用指向抽象基类的指针替换回调会影响决策吗?

在大多数情况下,最终也会成为动态调度。

回调设置为 nullptr 以外的东西的可能性如何。我猜如果回调很可能是 nullptr,那么使用附加分支会更快,对吧?

这将取决于比率以及硬件(与间接函数调用相比,分支是否更快;这也取决于比率)。


只是为了让您意识到事情是多么微妙:假设您正在谈论现代 x86_64 桌面处理器,其中调用没有被内联并且有 99% 的可能性是nullptr. 人们告诉你测量。

所以你做了,在这种情况下,让我们假设你发现分支似乎更快。伟大的!所以我们应该开始到处使用分支,对吧?

不是真的,因为您的分支预测器可能会使代码有效地免费(从未采用分支),这意味着您甚至根本没有比较分支与间接调用。

于 2019-09-30T17:11:44.230 回答