Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/141.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 检查bool是否比调用空函数更有效?_C++_Performance_Function_Optimization_Polymorphism - Fatal编程技术网

C++ 检查bool是否比调用空函数更有效?

C++ 检查bool是否比调用空函数更有效?,c++,performance,function,optimization,polymorphism,C++,Performance,Function,Optimization,Polymorphism,如果我有一个带有功能条的对象Foo,该功能条可能包含也可能不包含任何实际功能,那么首先检查公共bool成员HasBar以确定是否存在点调用条,或者直接调用该函数,如果它什么都不做,则立即让它返回,会更快吗 即给定: class Foo { public: bool HasBar; void Bar() { } }; 这样做是否更好: Foo myFoo; if (myFoo.HasBar) { myFoo.Bar(); } …或者这个: Foo myF

如果我有一个带有功能条的对象Foo,该功能条可能包含也可能不包含任何实际功能,那么首先检查公共bool成员HasBar以确定是否存在点调用条,或者直接调用该函数,如果它什么都不做,则立即让它返回,会更快吗

即给定:

class Foo
{
public:
    bool HasBar;
    void Bar()
    {
    }
};
这样做是否更好:

Foo myFoo;
if (myFoo.HasBar)
{
    myFoo.Bar();
}
…或者这个:

Foo myFoo;
myFoo.Bar();
通常,我认为编译器会对后者进行优化,但我们讨论的是由驻留在DLL中的工厂动态实例化的对象,这些工厂在运行时动态加载

因为我已经能感觉到过早的优化火炬和干草叉正在逼近,所以让我提供一些背景,除了我很想知道的事实之外,不管我在做什么:

这是引擎的一部分,其他应用程序将在其上运行,因此它的脚步越轻,任何优化都越好。将有一个多态Foo对象树,可能有数千个对象每秒迭代多次。每个迭代包括通过树的3个过程,在每个过程中,对每个对象调用不同的函数


例如,在第一个过程中,将对每个对象调用Bar1,在下一个Bar2过程中调用,在最后一个过程中调用Bar3。有些对象在这些函数中工作,有些则不然。迭代器不知道,因此它必须根据过程盲目地调用适当的函数。这就是问题背后的原因-它是否应该首先检查当前对象是否有意义地实现了相应的功能,例如,通过检查每个对象公开的标志,还是应该直接调用它?

如果没有进行优化,检查单个bool变量比输入函数便宜得多,在堆栈上分配内容并向后跳转。尽管这样的优化还为时过早。

如果没有进行优化,那么检查一个bool变量要比输入函数、在堆栈上分配内容和跳回便宜得多。尽管这样的优化还为时过早。

这样或那样应该没有太大的区别,因为你是在用有条件的交易换取即时回报的通话。由于您的动态库是以任何方式链接的,因此无论您的选择如何,您都要为链接付费

使用布尔保护的方法可以为您节省一些CPU周期的唯一情况是,函数采用必须在调用之前进行评估的参数,并且评估需要花费大量时间

不过,使用guard的方法有一个缺点:它在方法和布尔标志之间引入了耦合,因此实现这一点的人必须记住这种连接。它在代码中是不显式的,所以可以用一个很好的、很长的Bar实现来编写一个类,而不会仅仅因为忘记将Foo切换为true而调用它


最棒的是,这不是你需要事先决定的事情:编写代码并分析一个特别困难的案例来决定走哪条路。

这两种方式应该没有太大的区别,因为你是在用有条件的交易换取一个即时回报的通话。由于您的动态库是以任何方式链接的,因此无论您的选择如何,您都要为链接付费

使用布尔保护的方法可以为您节省一些CPU周期的唯一情况是,函数采用必须在调用之前进行评估的参数,并且评估需要花费大量时间

不过,使用guard的方法有一个缺点:它在方法和布尔标志之间引入了耦合,因此实现这一点的人必须记住这种连接。它在代码中是不显式的,所以可以用一个很好的、很长的Bar实现来编写一个类,而不会仅仅因为忘记将Foo切换为true而调用它


最棒的是,这并不是你需要预先决定的事情:编写代码并分析一个特别困难的案例,以决定该走哪条路。

我测试它,因为我对它很好奇。我不是C++专家,所以我只是测试了一些东西来混淆编译器,不要让他优化我的代码来破坏测试结果。我使用Visual Studio 2012在Win8机器上进行了测试:

福安

Foo.cpp

Foo::Foo(void)    {    }
Foo::~Foo(void)    {    }
int Foo::Bar()
{
   int a = 1, b = 1;
   return a + b;
} 
主要内容:

结果:

打电话给它是因为布尔是真的:32446 不要因为bool是假的而叫它:32713 不管怎么说,请拨打35224

如果bool为true或false,则函数调用比断言慢一点


顺序并不重要。如果我将call it anyway-test移到顶部,将出现类似的测试结果。函数调用的测试总是比bool的断言慢。

我测试它是因为我对它很好奇。我不是C++专家所以我JUS 不要测试一些东西来迷惑编译器,不要让他优化我的代码来破坏测试结果。我使用Visual Studio 2012在Win8机器上进行了测试:

福安

Foo.cpp

Foo::Foo(void)    {    }
Foo::~Foo(void)    {    }
int Foo::Bar()
{
   int a = 1, b = 1;
   return a + b;
} 
主要内容:

结果:

打电话给它是因为布尔是真的:32446 不要因为bool是假的而叫它:32713 不管怎么说,请拨打35224

如果bool为true或false,则函数调用比断言慢一点


顺序并不重要。如果我将call it anyway-test移到顶部,将出现类似的测试结果。函数调用的测试总是比bool的断言慢。

如果可以避免大部分调用,则检查bool会更快。检查bool比函数调用快很多,但是如果这样编写代码,代码当然就不那么优雅了

但是,如果您需要对批处理中的所有Foo对象调用此函数,那么有一种方法可以让每个人都满意:

尝试在DLL中使用一个预先分配的数组,该数组使用Foo对象填充,如果标志为true,则可能不需要再存储bool。这可以在通过Foo对象的一次过程中一次全部填充。在Foo对象上添加HasBar的getter/setter并将其从该容器中删除可能是最优雅的。您必须决定如何以适合您的代码的方式处理此细节

现在,假设您有这样一个数组,您可以在它的调用栏上循环数组中的所有内容

这有许多优点:

避免不必要的函数调用。 避免不必要的布尔检查。 当需要更改HasBar的值时,仅通过HasBar==true的Foo对象进行缩放。 使分支更可预测。 如果在数组中放置对象而不是指针,则引用的局部性更好。 优雅而高效的代码。
这对你来说更快吗?您必须尝试一下,看看。

如果可以避免大部分呼叫,那么检查bool会更快。检查bool比函数调用快很多,但是如果这样编写代码,代码当然就不那么优雅了

但是,如果您需要对批处理中的所有Foo对象调用此函数,那么有一种方法可以让每个人都满意:

尝试在DLL中使用一个预先分配的数组,该数组使用Foo对象填充,如果标志为true,则可能不需要再存储bool。这可以在通过Foo对象的一次过程中一次全部填充。在Foo对象上添加HasBar的getter/setter并将其从该容器中删除可能是最优雅的。您必须决定如何以适合您的代码的方式处理此细节

现在,假设您有这样一个数组,您可以在它的调用栏上循环数组中的所有内容

这有许多优点:

避免不必要的函数调用。 避免不必要的布尔检查。 当需要更改HasBar的值时,仅通过HasBar==true的Foo对象进行缩放。 使分支更可预测。 如果在数组中放置对象而不是指针,则引用的局部性更好。 优雅而高效的代码。
这对你来说更快吗?您必须尝试一下,看看。

检查布尔值比调用空函数更有效吗?-对这是正确的问题吗?可能不会。您可以检查函数内的值,也可以内联函数,将大型工作委托给另一个。@GavinH Subtext:如果是,范围如何。我真的不明白为什么问这样的问题——关于性能和隐含的,程序是如何执行的——是不对的。如果有人对某事感到好奇,我认为问是最好的解决方法。在我知道他们的答案或控制他们的机制之前,我不知道该问什么和不该问什么。@d7samurai您的具体案例中的差异只能通过试验和确定不同方法的时间来衡量。几乎在你写这个问题的时候,你就可以为自己得到一个明确的答案!如果听起来很刺耳,很抱歉,但这是最好的方式!有时,问题和答案不仅与结论有关,还与导致这些结论的考虑因素和过程有关。检查布尔值是否比调用空函数更有效对这是正确的问题吗?可能不会。您可以检查函数内的值,也可以内联函数,将大型工作委托给另一个。@GavinH Subtext:如果是,范围如何。我真的不明白为什么问这样的问题——关于性能和隐含的,程序是如何执行的——是不对的。如果有人对某事感到好奇,我认为问是最好的解决方法。在我知道他们的答案或控制他们的机制之前,我不知道该问什么和不该问什么。@d7samurai在你的具体案例中的区别

n只有通过实验和计时不同的方法才能真正衡量。几乎在你写这个问题的时候,你就可以为自己得到一个明确的答案!如果听起来很刺耳,很抱歉,但这是最好的方式!有时,问题和答案不仅与结论有关,还与导致结论的考虑因素和过程有关。我想另一个论点是,对于布尔守卫,在所有情况下,条件也会受到惩罚,因为它的计算结果为true,并且确实调用了函数。我同意你关于耦合的观点。另外,无论如何,我更喜欢盲呼叫的美学效果。@d7samurai也是如此,但与中等大小的function.PS的有效负载相比,它通常是无法检测到的。你能详细解释一下,不管你怎么选择,你都要为链接付费是什么意思吗?除了阻止编译器优化调用之外,链接还有什么影响?@d7samurai如果您可以使用来自动态链接库以外的源(例如,来自配置文件)的布尔标志,以避免在运行时完全加载动态库,这将是性能方面的一大胜利。但是,如果标志和函数都来自同一个库,则需要在运行时加载它,然后才能检查标志或调用函数。在任何情况下都必须加载库。DLL包含插件。插件是工厂,生产构成问题树的对象。主机对它们进行迭代,以便它们可以执行主机不知道的任何操作。DLL加载仅在应用程序启动时完成,而不是在迭代期间。函数调用的频率可能高达每秒100000次以上——这就是为什么我想知道调用中有一半的代价是立即返回。我想另一个论点是,使用布尔值保护,在所有情况下,条件值也会受到惩罚,因为它的计算结果为true,并且确实调用了函数。我同意你关于耦合的观点。另外,无论如何,我更喜欢盲呼叫的美学效果。@d7samurai也是如此,但与中等大小的function.PS的有效负载相比,它通常是无法检测到的。你能详细解释一下,不管你怎么选择,你都要为链接付费是什么意思吗?除了阻止编译器优化调用之外,链接还有什么影响?@d7samurai如果您可以使用来自动态链接库以外的源(例如,来自配置文件)的布尔标志,以避免在运行时完全加载动态库,这将是性能方面的一大胜利。但是,如果标志和函数都来自同一个库,则需要在运行时加载它,然后才能检查标志或调用函数。在任何情况下都必须加载库。DLL包含插件。插件是工厂,生产构成问题树的对象。主机对它们进行迭代,以便它们可以执行主机不知道的任何操作。DLL加载仅在应用程序启动时完成,而不是在迭代期间。函数调用的频率可能高达每秒100000次以上,这就是为什么我想知道调用中有一半的代价是立即返回。实际的系统比单个带条的foo数组复杂得多。。有X个DLL,所有这些DLL都包含Y个FOO变体,它们被动态实例化并添加到集合中,集合在运行时是一个基于用户执行的操作的树。此外,在树上执行多个过程,一些调用Foo,一些不调用Foo,并且与其他函数类似,这取决于过程。一个解决方案当然是为每个过程保留一个单独的树,只包含那些表现出该过程相关行为的foo。但我的问题更一般,将两种方法的相对性能作为一个起点,以便我能够准确地做出这样的架构选择。@d7samurai,我明白你的意思。我最想指出的是,如果这是一个瓶颈,有一些方法可以完全避免这个问题;因此,实际上,将这两种方法进行对比并不会起作用。当然,这种方法为需要分析成本/收益的问题引入了更多的维度。@d7samurai,检查bool更快,因此这里有另一种想法可以使它更优雅:可能将worker函数的外壳放在头文件中,即检查bool的部分。这段代码将编译到它所包含的任何模块中,并且也将被内联。在检查BoL之后,这可以调用一个编译成DLL本身的C++文件。这将以一种优雅的方式封装功能,并在实际函数调用(至少是生成函数的函数调用)之前通过初步bool检查加快速度
调用程序集。实际系统比单个带条的foo数组复杂得多。。有X个DLL,所有这些DLL都包含Y个FOO变体,它们被动态实例化并添加到集合中,集合在运行时是一个基于用户执行的操作的树。此外,在树上执行多个过程,一些调用Foo,一些不调用Foo,并且与其他函数类似,这取决于过程。一个解决方案当然是为每个过程保留一个单独的树,只包含那些表现出该过程相关行为的foo。但我的问题更一般,将两种方法的相对性能作为一个起点,以便我能够准确地做出这样的架构选择。@d7samurai,我明白你的意思。我最想指出的是,如果这是一个瓶颈,有一些方法可以完全避免这个问题;因此,实际上,将这两种方法进行对比并不会起作用。当然,这种方法为需要分析成本/收益的问题引入了更多的维度。@d7samurai,检查bool更快,因此这里有另一种想法可以使它更优雅:可能将worker函数的外壳放在头文件中,即检查bool的部分。这段代码将编译到它所包含的任何模块中,并且也将被内联。在检查BoL之后,这可以调用一个编译成DLL本身的C++文件。这将以一种优雅的方式封装功能,并在实际函数调用(至少是生成函数调用程序集的函数调用)之前通过初步bool检查加快速度。
int _tmain(int argc, _TCHAR* argv[])
{
{

    unsigned int start = clock();




    // call it because the bool is true
    for(int i = 0; i < 100000000; i++){

        bool trueBool;
        int sum = 0;
        int expected = 0;
        for(int j = i; j < 100 + i; j++){
            sum +=j;
            expected +=j;
        }
        trueBool = sum == expected;

        Foo f1;
        int returnedNumber;
        if(f1.HasBar){
                            // same code as in Bar()
            int a = 1, b = 1;
            returnedNumber = a + b;
        }
    }

    unsigned int end = clock();


    printf("call it because the bool is true: %d",   end - start);
}

{
    unsigned int start = clock();

    // dont call it
    for(int i = 0; i < 100000000; i++){

        bool trueBool;
        int sum = 0;
        int expected = 0;
        for(int j = i; j < 100 + i; j++){
            sum +=j;
            expected +=j;
        }
        trueBool = sum != expected;

        Foo f1;
        int returnedNumber;
        if(f1.HasBar){
                            // same code as in Bar()
            int a = 1, b = 1;
            returnedNumber = a + b;
        }

    }

    unsigned int end = clock();


    printf("dont call it: %d",   end - start);

}

{
    unsigned int start = clock();

    // call it anyway
    for(int i = 0; i < 100000000; i++){

        bool trueBool;
        int sum = 0;
        int expected = 0;
        for(int j = i; j < 100 + i; j++){
            sum +=j;
            expected +=j;
        }
        trueBool = sum == expected;

        Foo f1;
        int returnedNumber;
        returnedNumber = f1.Bar();

    }

    unsigned int end = clock();
    printf("call it anyway: %d",   end - start);
}

return 0;
}