簡體   English   中英

使用混合const /非const三元運算符進行編譯時

[英]Compile-time with mixed const/non-const ternary operator

考慮以下示例:

template<int X> class MyClass
{
    public:
        MyClass(int x) {_ncx = x;}
        void test() 
        {
            for (unsigned int i = 0; i < 1000000; ++i) {
                if ((X < 0) ? (_cx > 5) : (_ncx > 5)) {
                    /* SOMETHING */
                } else {
                    /* SOMETHING */
                }
            }
        }
    protected:
        static const int _cx = (X < 0) ? (-X) : (X);
        int _ncx;
};

我的問題是:MyClass <-6> :: test()和MyClass <6> :: test()會有不同的速度嗎?

我希望是這樣,因為如果模板參數為負, if可以在編譯時評估測試中的if in函數,但是我不確定如果有編譯時事件並且非編譯時,編譯器的行為是什么?三元運算符中的編譯時事物(這里就是這種情況)。

注意:這是一個純粹的“理論”問題。 如果非空概率為“是”,則將使用此類編譯時模板參數為我的代碼實現某個類,否則,我將僅提供運行時版本。

將條件移到循環外:

        ...
        if ((X < 0) ? (_cx > 5) : (_ncx > 5)) {
            for (unsigned int i = 0; i < 1000000; ++i) {
                /* SOMETHING */
            }
        } else {
            for (unsigned int i = 0; i < 1000000; ++i) {
                /* SOMETHING */
            }
        }
        ...

這樣一來,您就不必依賴編譯器優化來刪除未使用的代碼。 如果編譯器沒有刪除條件的未使用部分,則只需為條件分支支付一次,而不是每次循環都需要。

對於我的編譯器(OS X上為clang ++ v2.9),編譯此相似但不相同的代碼:

void foo();
void bar();

template<int N>
void do_something( int arg ) {
  if ( N<0 && arg<0 ) { foo(); }
  else { bar(); }
}

// Some functions to instantiate the templates.
void one_fn(int arg) {
  do_something<1>(arg);
}

void neg_one_fn(int arg) {
  do_something<-1>(arg);
}

這將使用clang++ -S -O3生成以下程序集。

one_fn = do_something <1>

顯然,第一個功能組件只有對bar的調用。

    .globl  __Z6one_fni
    .align  4, 0x90
__Z6one_fni:                            ## @_Z6one_fni
Leh_func_begin0:
    pushl   %ebp
    movl    %esp, %ebp
    popl    %ebp
    jmp __Z3barv                ## TAILCALL
Leh_func_end0:

neg_one_fn = do_something <-1>

如果調用barfoo則第二個函數已簡化為一個簡單的函數。

    .globl  __Z10neg_one_fni
    .align  4, 0x90
__Z10neg_one_fni:                       ## @_Z10neg_one_fni
Leh_func_begin1:
    pushl   %ebp
    movl    %esp, %ebp
    cmpl    $0, 8(%ebp)
    jns LBB1_2                  ## %if.else.i
    popl    %ebp
    jmp __Z3foov                ## TAILCALL
LBB1_2:                                 ## %if.else.i
    popl    %ebp
    jmp __Z3barv                ## TAILCALL
Leh_func_end1:

摘要

因此,您可以看到編譯器內聯了模板,然后在可能的情況下優化了分支。 因此,您希望進行的轉換確實發生在當前的編譯器中。 我也從舊的g ++ 4.0.1編譯器中獲得了相似的結果(但匯編過程不太清晰)。

附錄:

我認為此示例與您的初始情況不太相似(因為它不涉及三元運算符),因此我將其更改為:(獲得相同的結果)

template<int X>
void do_something_else( int _ncx ) {
  static const int _cx = (X<0) ? (-X) : (X);
  if ( (X < 0) ? (_cx > 5) : (_ncx > 5)) {
    foo();
  } else {
    bar();
  }
}

void a(int arg) {
  do_something_else<1>(arg);
}

void b(int arg) {
  do_something_else<-1>(arg);
}

這生成了程序集

a()= do_something_else <1>

這仍然包含分支。

__Z1ai:                                 ## @_Z1ai
Leh_func_begin2:
    pushl   %ebp
    movl    %esp, %ebp
    cmpl    $6, 8(%ebp)
    jl  LBB2_2                  ## %if.then.i
    popl    %ebp
    jmp __Z3foov                ## TAILCALL
LBB2_2:                                 ## %if.else.i
    popl    %ebp
    jmp __Z3barv                ## TAILCALL
Leh_func_end2:

b()= do_something_else <-1>

分支被優化掉了。

__Z1bi:                                 ## @_Z1bi
Leh_func_begin3:
    pushl   %ebp
    movl    %esp, %ebp
    popl    %ebp
    jmp __Z3barv                ## TAILCALL
Leh_func_end3:

這可能取決於編譯器的智能程度。 我建議您編寫一個基准測試程序以在您的環境中對其進行測試,以確保確定。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM