Что быстрее: if(bool) или if (int)?
какое значение лучше использовать? Логическое значение true или целое число 1?
выше тема заставила меня сделать некоторые эксперименты с bool и int на if состояние. Поэтому просто из любопытства я написал эту программу:
int f(int i)
{
if ( i ) return 99; //if(int)
else return -99;
}
int g(bool b)
{
if ( b ) return 99; //if(bool)
else return -99;
}
int main(){}
g++ intbool.cpp -S генерирует ассемблерный код для каждой функции следующим образом:
ASM код для
f(int)
__Z1fi:
LFB0:
pushl %ebp
LCFI0:
movl %esp, %ebp
LCFI1:
cmpl , 8(%ebp)
je L2
movl , %eax
jmp L3
L2:
movl $-99, %eax
L3:
leave
LCFI2:
ret
ASM код для
g(bool)
__Z1gb:
LFB1:
pushl %ebp
LCFI3:
movl %esp, %ebp
LCFI4:
subl , %esp
LCFI5:
movl 8(%ebp), %eax
movb %al, -4(%ebp)
cmpb , -4(%ebp)
je L5
movl , %eax
jmp L6
L5:
movl $-99, %eax
L6:
leave
LCFI6:
ret
удивительно, g(bool) создает более asm инструкции! Значит ли это, что if(bool) немного медленнее, чем if(int)? Я раньше думал bool специально разработан для использования в условном операторе, таком как if, так что я ожидал g(bool) чтобы генерировать меньше инструкций asm, тем самым делая g(bool) более эффективный и быстрый.
EDIT:
на данный момент я не использую флаг оптимизации. Но даже отсутствие его, почему он генерирует больше asm для g(bool) это вопрос, на который я ищу разумного ответа. Я должен также сказать вам, что -O2 флаг оптимизации генерирует точно такой же asm. Но вопрос не в этом. Вопрос в том, что я просил.
8 ответов:
имеет смысл для меня. Ваш компилятор, по-видимому, определяет
boolКак 8-битное значение, и ваша система ABI требует, чтобы она" продвигала " небольшие (bool, компилятор генерирует код для выделения наименее значимого байта 32-разрядного аргумента, который получает g, и сравнивает его сcmpb. В первом примереintаргумент использует полные 32 бита, которые были помещены в стек, поэтому он просто сравнивает против всего этого сcmpl.
компиляция с
-03дает следующее Для меня:f:
pushl %ebp movl %esp, %ebp cmpl , 8(%ebp) popl %ebp sbbl %eax, %eax andb , %al addl , %eax retg:
pushl %ebp movl %esp, %ebp cmpb , 8(%ebp) popl %ebp sbbl %eax, %eax andb , %al addl , %eax ret.. таким образом, он компилируется по существу в один и тот же код, за исключением
cmplvscmpb. Это означает, что разница, если она есть, не имеет значения. Судя по неоптимизированному коду это не справедливо.
Edit чтобы прояснить мою точку зрения. Неоптимизированный код предназначен для простой отладки, а не для скорости. Сравнение скорости неоптимизированного кода бессмысленный.
когда я компилирую это с разумным набором опций (в частности-O3), вот что я получаю:
на
f():.type _Z1fi, @function _Z1fi: .LFB0: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 cmpl , %edi sbbl %eax, %eax andb , %al addl , %eax ret .cfi_endprocна
g():.type _Z1gb, @function _Z1gb: .LFB1: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 cmpb , %dil sbbl %eax, %eax andb , %al addl , %eax ret .cfi_endprocони все еще используют различные инструкции для сравнения (
cmpbдля логических иcmplдля int), но в остальном тел одинаковы. Быстрый взгляд на руководства Intel говорит мне:... ничего особенного. Нет такой вещи, какcmpbилиcmplв руководствах Intel. Они всеcmpи я не могу найти временные таблицы на данный момент. Я предполагаю, однако, что нет никакой разницы в часах между сравнением байта немедленного и сравнением длинного немедленного, поэтому для всех практических целей код идентичен.
отредактировано, чтобы добавить следующее на основе вашего добавления
причина, по которой код отличается в неоптимизированном случае, заключается в том, что он неоптимизирован. (Да, это круг, я знаю.), Когда компилятор ходит AST и генерирует код напрямую, он ничего не "знает", кроме того, что находится в непосредственной точке AST, в которой он находится. В этот момент ему не хватает всей контекстной информации, необходимой для того, чтобы знать, что в этот конкретный момент он может обрабатывать объявленный тип
boolкакint. Логическое значение, очевидно, по умолчанию рассматривается как байт, и при манипулировании байтами в мире Intel вам нужно делать такие вещи, как расширение знака, чтобы довести его до определенной ширины, чтобы поместить его в стек и т. д. (Вы не можете нажать на байт.)когда оптимизатор просматривает AST и делает свою магию, однако, он смотрит на окружающий контекст и "знает", когда он может заменить код на что-то более эффективное без изменения семантики. Таким образом, он "знает", что он может использовать целое число в параметре и тем самым потерять ненужные преобразования и расширение.
С GCC 4.5 на Linux и Windows, по крайней мере,
sizeof(bool) == 1. На x86 и x86_64 вы не можете передать в функцию меньше, чем значение регистра общего назначения (будь то через стек или регистр в зависимости от соглашения о вызове и т. д...).таким образом, код для bool, когда он не оптимизирован, фактически идет на некоторую длину, чтобы извлечь это значение bool из стека аргументов (используя другой слот стека для сохранения этого байта). Это сложнее, чем просто вытащить собственный регистровый размер переменная.
на машинном уровне нет такой вещи, как bool
очень немногие архитектуры наборов команд определяют какой-либо тип логического операнда, хотя часто существуют инструкции, которые вызывают действие на ненулевые значения. Для процессора, как правило, все является одним из скалярных типов или строкой из них.
заданный компилятор и заданный ABI должны будут выбрать определенные размеры для
intиboolи когда, как в вашем случае, это разные размеры, они могут создать немного другой код, и на некоторых уровнях оптимизации может быть немного быстрее.почему bool один байт на многих системах?
безопаснее выбрать
charтип для типа bool, потому что кто-то может сделать действительно большой массив из них.обновление: by "безопаснее", я имею в виду:для компилятора и библиотек-исполнителей. я не говорю, что люди должны переопределить тип системы.
Да, обсуждение весело. Но просто проверьте это:
тестовый код:
#include <stdio.h> #include <string.h> int testi(int); int testb(bool); int main (int argc, char* argv[]){ bool valb; int vali; int loops; if( argc < 2 ){ return 2; } valb = (0 != (strcmp(argv[1], "0"))); vali = strcmp(argv[1], "0"); printf("Arg1: %s\n", argv[1]); printf("BArg1: %i\n", valb ? 1 : 0); printf("IArg1: %i\n", vali); for(loops=30000000; loops>0; loops--){ //printf("%i: %i\n", loops, testb(valb=!valb)); printf("%i: %i\n", loops, testi(vali=!vali)); } return valb; } int testi(int val){ if( val ){ return 1; } return 0; } int testb(bool val){ if( val ){ return 1; } return 0; }компилируется на 64-битном ноутбуке Ubuntu 10.10 с: g++ - O3-o / tmp / test_i /tmp/test_i.cpp
сравнение на основе целых чисел:
sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m8.203s user 0m8.170s sys 0m0.010s sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m8.056s user 0m8.020s sys 0m0.000s sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m8.116s user 0m8.100s sys 0m0.000sлогический тест / напечатать комментарии (и целое комментирует):
sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m8.254s user 0m8.240s sys 0m0.000s sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m8.028s user 0m8.000s sys 0m0.010s sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m7.981s user 0m7.900s sys 0m0.050sони одинаковы с 1 назначением и 2 сравнениями каждый цикл более 30 миллионов циклов. Найдите что-то еще для оптимизации. Например, не используйте strcmp без необходимости. ;)
Это будет зависеть от компилятора и оптимизации. Здесь есть интересная дискуссия (язык агностик):
требуется ли "if ([bool] == true)" на один шаг больше, чем "if ([bool])"?
кроме того, взгляните на этот пост: http://www.linuxquestions.org/questions/programming-9/c-compiler-handling-of-boolean-variables-290996/
подходим к вашему вопросу двумя разными способами:
Если вы конкретно говорите о C++ или любом языке программирования, который будет производить ассемблерный код по этому вопросу, мы привязаны к тому, какой код компилятор будет генерировать в ASM. Мы также связаны с представлением истинного и ложного в c++. Целое число должно быть сохранено в 32 битах, и я мог бы просто использовать байт для хранения логического выражения. Фрагменты Asm для условных операторов:
для целое число:
mov eax,dword ptr[esp] ;Store integer cmp eax,0 ;Compare to 0 je false ;If int is 0, its false ;Do what has to be done when true false: ;Do what has to be done when falseдля типа bool:
mov al,1 ;Anything that is not 0 is true test al,1 ;See if first bit is fliped jz false ;Not fliped, so it's false ;Do what has to be done when true false: ;Do what has to be done when falseТак вот почему сравнение скорости компиляции зависит. В приведенном выше случае bool будет немного быстрым, так как
cmpбудет означать вычитание для установки флагов. Это также противоречит тому, что сгенерировал ваш компилятор.другой подход, гораздо более простой, заключается в том, чтобы посмотреть на логику выражения самостоятельно и попытаться не беспокоиться о том, как компилятор будет переводить ваш код, и я думаю это гораздо более здоровый образ мышления. Я все еще верю, в конечном счете, что код, генерируемый компилятором, на самом деле пытается дать правдивое разрешение. Я имею в виду, что, возможно, если вы увеличите тестовые случаи в операторе if и придерживаетесь boolean в одной стороне и integer в другой, компилятор сделает это так, чтобы сгенерированный код выполнялся быстрее с булевыми выражениями на машинном уровне.
Я рассматриваю это концептуальный вопрос, поэтому я дам концептуальный ответ. Это обсуждение напоминает мне о дискуссиях, которые я обычно имею о том, переводит ли эффективность кода на меньшее количество строк кода в сборке. Кажется, что эта концепция общепринята как истинная. Учитывая, что отслеживание того, как быстро ALU будет обрабатывать каждый оператор, не является жизнеспособным, второй вариант-сосредоточиться на прыжках и сравнении в сборке. Когда это так, различие между булевыми операторами или целыми числами в представленном коде становится весьма представительным. Результат выражения в C++ возвращает значение, которое затем будет дано представление. С другой стороны, в сборке переходы и сравнения будут основываться на числовых значениях независимо от того, какой тип выражения вычислялся в инструкции C++ if. Важно помнить, что чисто логические утверждения, такие как эти, приводят к огромным вычислительным затратам, даже если один бит будет способен к тому же вещь.
Comments