Время компиляции со смешанным константным / неконстантным тернарным оператором

Рассмотрим следующий пример:

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 в тестовой функции может быть оценена во время компиляции, но я не уверен, каково поведение компилятора, если в троичном операторе есть вещь времени компиляции и вещь не компиляции (что имеет место здесь ).

Примечание: это чисто «теоретический» вопрос. Если вероятность «да» не равна нулю, я реализую некоторый класс для моего кода с такими параметрами шаблона во время компиляции, а если нет, я буду предоставлять только версии во время выполнения.

2

Решение

Для моего компилятора (clang ++ v2.9 на OS X) компилируем этот похожий, но не идентичный код:

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>

Вторая функция была уменьшена до простой, если вызвать bar или же foo,

    .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:
2

Другие решения

Переместите условное за пределы цикла:

        ...
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 */
}
}
...

Таким образом, вы не зависите от оптимизации компилятора для удаления неиспользуемого кода; если неиспользуемая часть условия не удаляется компилятором, вы просто платите за условную ветвь один раз, а не каждый раз за цикл.

2

Это, вероятно, зависит от того, насколько умен ваш компилятор. Я рекомендую вам написать небольшую тестовую программу, чтобы самостоятельно проверить ее в своей среде, чтобы убедиться в этом.

0
По вопросам рекламы [email protected]