Какое значение лучше использовать? Логическое истина или целое число 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-код для каждой функции следующим образом:
asm-код для
f(int)
__Z1fi: LFB0: pushl %ebp LCFI0: movl %esp, %ebp LCFI1: cmpl $0, 8(%ebp) je L2 movl $99, %eax jmp L3 L2: movl $-99, %eax L3: leave LCFI2: ret
asm-код для
g(bool)
__Z1gb: LFB1: pushl %ebp LCFI3: movl %esp, %ebp LCFI4: subl $4, %esp LCFI5: movl 8(%ebp), %eax movb %al, -4(%ebp) cmpb $0, -4(%ebp) je L5 movl $99, %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)
более эффективным и быстрым.
РЕДАКТИРОВАТЬ:
На данный момент я не использую никаких флагов оптимизации. Но даже при его отсутствии, почему он генерирует больше asm для g(bool)
- это вопрос, на который я ищу разумный ответ. Я также должен вам сказать, что -O2
флаг оптимизации генерирует точно такой же asm. Но вопрос не в этом. Вопрос в том, что я задал.
g(bool)
- это вопрос, на который я ищу разумный ответ.Ответы:
Для меня это имеет смысл. Ваш компилятор, по-видимому, определяет a
bool
как 8-битное значение, а ABI вашей системы требует, чтобы он «продвигал» маленькие (<32-битные) целочисленные аргументы до 32-битных при их добавлении в стек вызовов. Итак, чтобы сравнить abool
, компилятор генерирует код для выделения младшего байта 32-битного аргумента, который получает g, и сравнивает его сcmpb
. В первом примереint
аргумент использует полные 32 бита, которые были помещены в стек, поэтому он просто сравнивается со всем этим сcmpl
.источник
__int64
быстрее, чемint
? Или процессор обрабатывает 32-битные целые числа с 32-битными наборами инструкций отдельно?Компиляция с помощью
-03
дает мне следующее:f:
pushl %ebp movl %esp, %ebp cmpl $1, 8(%ebp) popl %ebp sbbl %eax, %eax andb $58, %al addl $99, %eax ret
грамм:
pushl %ebp movl %esp, %ebp cmpb $1, 8(%ebp) popl %ebp sbbl %eax, %eax andb $58, %al addl $99, %eax ret
.. поэтому он компилирует по существу тот же самый код, за исключением
cmpl
противcmpb
. Это означает, что разница, если она есть, не имеет значения. Судить по неоптимизированному коду нечестно.Отредактируйте, чтобы прояснить мою точку зрения. Неоптимизированный код предназначен для простой отладки, а не для скорости. Сравнивать скорость неоптимизированного кода бессмысленно.
источник
cmpl
для одного, аcmpb
для другого?bool
- это один байт, а anint
- четыре. Я не думаю, что есть что-то более особенное, чем это.bool
как 8-битный тип.char
это байт по определению и наименьшая адресуемая единица.bool
размер определяется реализацией и может быть 1, 4, 8 или что угодно. Однако компиляторы, как правило, делают это одним.Когда я компилирую это с разумным набором параметров (в частности, -O3), вот что я получаю:
Для
f()
:.type _Z1fi, @function _Z1fi: .LFB0: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 cmpl $1, %edi sbbl %eax, %eax andb $58, %al addl $99, %eax ret .cfi_endproc
Для
g()
:.type _Z1gb, @function _Z1gb: .LFB1: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 cmpb $1, %dil sbbl %eax, %eax andb $58, %al addl $99, %eax ret .cfi_endproc
Они по-прежнему используют разные инструкции для сравнения (
cmpb
для логических иcmpl
для int), но в остальном тела идентичны. Беглый взгляд на руководства Intel говорит мне: ... ничего особенного. В руководствах Intel нет такого понятия, какcmpb
илиcmpl
. Они все,cmp
и я пока не могу найти таблицы времени. Однако я предполагаю, что нет разницы в часах между сравнением непосредственного байта и сравнением длинного немедленного, поэтому для всех практических целей код идентичен.отредактировал, чтобы добавить следующее на основе вашего добавления
Причина, по которой код отличается в неоптимизированном случае, заключается в том, что он не оптимизирован. (Да, это циклично, я знаю.) Когда компилятор обходит AST и напрямую генерирует код, он ничего не «знает», кроме того, что находится в непосредственной точке AST, в котором он находится. В этот момент ему не хватает всей необходимой контекстной информации. чтобы знать, что в этот конкретный момент он может рассматривать объявленный тип
bool
какint
. Очевидно, что логическое значение по умолчанию обрабатывается как байт, и при манипулировании байтами в мире Intel вы должны делать такие вещи, как расширение знака, чтобы довести его до определенной ширины, чтобы поместить его в стек и т. Д. (Вы не можете протолкнуть байт .)Однако, когда оптимизатор просматривает AST и творит чудеса, он смотрит на окружающий контекст и «знает», когда он может заменить код чем-то более эффективным без изменения семантики. Таким образом, он «знает», что может использовать целое число в параметре и, таким образом, потерять ненужные преобразования и расширения.
источник
l
иb
суффиксы используются только в синтаксисе AT&T. Они просто относятся к версиямcmp
использования 4-байтовых (длинных) и 1-байтовых (байтовых) операндов соответственно. Если есть какая - либо двусмысленность в синтаксисе Intel, обычно операнд памяти с тегамиBYTE PTR
,WORD PTR
илиDWORD PTR
вместо того , чтобы положить суффикс опкода.cmp
имеют одинаковую производительность, и нет никаких штрафов за чтение частичного регистра%dil
. (Но это не останавливает лязг от забавного создания лабиринта с частичным регистром, используя размер байтаand
на AL как часть безотходного переключения регистра между 99 и -99.)По крайней мере, с GCC 4.5 в Linux и Windows,
sizeof(bool) == 1
. На x86 и x86_64 вы не можете передать функции меньше, чем значение регистра общего назначения (будь то через стек или регистр, в зависимости от соглашения о вызовах и т. Д.).Таким образом, код для bool, когда он не оптимизирован, фактически достигает некоторой длины, чтобы извлечь это значение bool из стека аргументов (используя другой слот стека для сохранения этого байта). Это сложнее, чем просто получить переменную размера собственного регистра.
источник
sizeof(bool)
иsizeof(wchar_t)
в реализации. » Так говоря ,sizeof(bool) == 1
не совсем корректно , если вы говорите о конкретной версии конкретного компилятора.На машинном уровне такого понятия, как bool, не существует.
Очень немногие архитектуры набора инструкций определяют какой-либо тип логического операнда, хотя часто есть инструкции, которые запускают действие с ненулевыми значениями. Для процессора обычно все является одним из скалярных типов или их строкой.
Данный компилятор и данный ABI нужно будет выбрать размеры , специфичные для
int
иbool
и когда, как в вашем случае, это разные размеры , они могут генерировать несколько иной код, и на некоторых уровнях оптимизации один может быть немного быстрее.Почему во многих системах bool имеет один байт?
Безопаснее выбирать
char
тип для bool, потому что кто-то может создать действительно большой массив.Обновление: под «безопаснее» я имею в виду: для разработчиков компилятора и библиотеки. Я не говорю, что людям нужно заново реализовать тип системы.
источник
bool
были представлены битами; так что байт будет хорошим компромиссом для скорости / компактности данных во многих реализациях.char
вместоbool
», а просто использовал «char
тип» для обозначения «1 байт», когда имел в виду размер, который компилятор выбирает дляbool
объектов.Да, обсуждение весело. Но просто проверьте это:
Код теста:
#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
Вот почему сравнение скорости так зависит от компиляции. В приведенном выше случае логическое значение будет немного быстрым, поскольку
cmp
подразумевает вычитание для установки флагов. Это также противоречит тому, что сгенерировал ваш компилятор.Другой подход, гораздо более простой, состоит в том, чтобы взглянуть на логику выражения как таковую и постараться не беспокоиться о том, как компилятор переведет ваш код, и я думаю, что это гораздо более здоровый образ мышления. В конечном итоге я все еще верю, что код, создаваемый компилятором, на самом деле пытается дать правдивое решение. Я имею в виду, что, возможно, если вы увеличите количество тестовых случаев в операторе if и будете использовать логическое значение с одной стороны и целое число с другой, компилятор сделает так, чтобы сгенерированный код выполнялся быстрее с логическими выражениями на машинном уровне.
Я считаю, что это концептуальный вопрос, поэтому дам концептуальный ответ. Это обсуждение напоминает мне о дискуссиях, которые я обычно веду о том, приводит ли эффективность кода к меньшему количеству строк кода в сборке. Кажется, что эта концепция общепринята как истинная. Учитывая, что отслеживать, насколько быстро ALU будет обрабатывать каждый оператор, нецелесообразно, второй вариант - сосредоточиться на переходах и сравнении в сборке. В этом случае различие между логическими операторами и целыми числами в представленном вами коде становится довольно характерным. Результат выражения в C ++ вернет значение, которому затем будет дано представление. С другой стороны, в сборке переходы и сравнения будут основаны на числовых значениях независимо от того, какой тип выражения оценивался в вашем C ++ операторе if. При ответе на эти вопросы важно помнить, что чисто логические утверждения, подобные этим, приводят к огромным вычислительным затратам, даже если один бит способен на то же самое.
источник