代码之家  ›  专栏  ›  技术社区  ›  cppBeginner

优化编译时应该知道的if-else,但信息在不同的项目中

  •  0
  • cppBeginner  · 技术社区  · 6 年前

    如何使用户定义模板功能的“行为模式” doIt(a,b) 是否会从另一个用户定义变量/函数 op 优化 如果还有其他的吗?

    这是一个正在工作的MCVE。
    我的VS解决方案有两个项目。 pj1 这是我的图书馆。 pj2 是用户的项目。

    A_pj1。H

    #pragma once
    template<int T>class BoolT{public:
        static bool op;
    };
    template<int T> bool BoolT<T>::op=true; //by default,  true=+, false=-
    template<int i> int doIt(int a,int b){
        if(BoolT<i>::op){
            return a+b;
        }else{ return a-b;}
    }
    

    一个用户定义。H

    #pragma once
    #include "A_pj1.h"
    inline void A_pj2_UserDefine_Reg(){
        BoolT<2>::op=false; //override default value;
    }
    

    A_pj2_main。cpp

    #include "A_pj2_UserDefine.h"
    #include <iostream>
    int main(){
        A_pj2_UserDefine_Reg();
        int s1=doIt<1>(3,2); //= 5 (correct)
        int s2=doIt<2>(3,2); //= 1 (correct)
        std::cout << s1<<" "<<" "<<s2<<std::endl;
        int asfasd=0;
    }
    

    (编辑)以下是反汇编(优化版本):-

    int s2=doIt<2>(3,2);
        std::cout << s1<<" "<<" "<<s2<<std::endl;
    00B61620  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0B6B0A8h)]  
        int s1=doIt<1>(3,2); //:
    00B61626  xor         eax,eax  
    00B61628  cmp         byte ptr [BoolT<1>::op (0B6E001h)],al  
        int s2=doIt<2>(3,2);
        std::cout << s1<<" "<<" "<<s2<<std::endl;
    00B6162E  push        offset std::endl<char,std::char_traits<char> > (0B61530h)  
    00B61633  push        1  
        int s1=doIt<1>(3,2); //:
    00B61635  setne       al  
        A_pj2_UserDefine_Reg();
    00B61638  mov         byte ptr [BoolT<2>::op (0B6E000h)],0  
        int s2=doIt<2>(3,2);
        std::cout << s1<<" "<<" "<<s2<<std::endl;
    00B6163F  push        offset string " " (0B6B220h)  
    00B61644  push        offset string " " (0B6B220h)  
        int s1=doIt<1>(3,2); //:
    00B61649  lea         eax,[eax*4+1]  
        int s2=doIt<2>(3,2);
        std::cout << s1<<" "<<" "<<s2<<std::endl;
    00B61650  push        eax  
    00B61651  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B6B0B4h)]  
    00B61657  push        eax  
    00B61658  call        std::operator<<<std::char_traits<char> > (0B61310h)  
    00B6165D  add         esp,8  
    00B61660  push        eax  
    00B61661  call        std::operator<<<std::char_traits<char> > (0B61310h)  
    00B61666  add         esp,8  
    00B61669  mov         ecx,eax  
    00B6166B  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B6B0B4h)]  
    00B61671  mov         ecx,eax  
    00B61673  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B6B0B8h)]  
        int asfasd=0;
    }
    00B61679  xor         eax,eax  
    00B6167B  ret  
    

    问题

    这个 doIt(a,b) 经常调用(>每秒60000次)。
    我很好奇 if(BoolT<i>::op) 可以进行优化。
    如果条件不适合管道计算。

    以下是我的程序行为可能会有所帮助:-

    • 访问写入 BoolT<2>::op 始终仅在课程开始时出现(例如:。 A_pj2_UserDefine_Reg() ).
    • 函数查询 布尔特<2>::op (直接和间接)几乎总是只发生在项目中 pj2 .
    • 用户无法编辑 pj1 .
    • 事实上,除此之外 A_pj2_main.cpp ,有很多 .cpp 那个电话 pj1 是的 doIt<>() .以下是包含图:-

    enter image description here

    我糟糕的解决方案

    B_pj1。H

    #pragma once
    template<int i> bool op(){return true;}
    template<int i> int doIt(int a,int b){
        if(op<i>()){
            return a+b;
        }else{ return a-b;}
        // return a+b;
    }
    

    B_pj2_UserDefine。cpp

    #include "B_pj1.h"
    template<> bool op<2>(){return false;}
    

    B_pj2_main。cpp

    #include <iostream>
    #include "B_pj1.h"
    int main(){
        int s1=doIt<1>(3,2); //:
        int s2=doIt<2>(3,2);
        std::cout << s1<<" "<<" "<<s2<<std::endl;
        int asfasd=0;
    }
    

    这个程序格式不好。(参考: Storing C++ template function definitions in a .CPP file )

    错误LNK2005“bool_ucdecl op<2>(void)”(??$op@$01@@YA_NXZ)已经存在 在B_pj2_UserDefine中定义。obj

    2 回复  |  直到 6 年前
        1
  •  1
  •   Soonts    6 年前

    你现在的代码很好。如果条件每次都采用不同的分支,则只会损害管道计算。当这个标志只写一次时,分支预测做得非常好。

    总之,你的问题的常见解决方案是C预处理器宏。在技术上可以解决的问题。 if constexpr 但实际上,这需要图书馆 #include 来自用户项目的东西,大多数库作者不想支持这样的用例。

    如果你只想制作库头,也可以用模板来解决,但要复杂一些。由于编译时间开销等原因,我尽量避免模板跨越库边界。

        2
  •  0
  •   JVApen    6 年前

    我认为这个例子太过简化,看不出它的意图。所以让我假设你有充分的理由这么做。

    首先,确保您正在MSVC上进行优化,这将是在命令行中添加/O2(或类似内容)。

    第二,衡量。CPU中的分支预测器非常有效,您可能甚至没有注意到。

    也就是说,当编译器看到 if (false) 它没有优化它,你可以认为它是编译器中的一个bug。不断折叠,尤其是当使用SSA作为模型时,这些分支应该很快消失,这非常简单。

    当有疑问时,或者当您想在编译时强制它时,使用 if constexpr 这就是解决办法。它确实需要C++17,并在MSVC2017中提供。

    另一种选择是将函数设置为constexpr,这可能并不总是可行的,尽管它看起来是这样的:

    template<int i> constexpr int doIt(int a,int b){
        if(BoolT<i>::op){
            return a+b;
        }else{ return a-b;}
    }
    

    你可以这样称呼它:

    constexpr int s1=doIt<1>(3,2);
    int s2=doIt<2>(3,i);
    

    这样,编译器就需要在编译时计算s1的值。而它可以在运行时重用该函数来计算s2。(注意变量i被传递)

    关于链接器错误,C++中有一个ODR(一个定义规则)。您可以通过添加内联来绕过它,但是,您应该在任何地方都有相同的实现!我不建议将实现放在CPP文件中,因为它是UB的收据。