Nilai mana yang lebih baik untuk digunakan? Boolean true atau Integer 1?
Topik di atas membuat saya melakukan beberapa percobaan dengan bool
dan int
dalam if
kondisi. Jadi hanya karena penasaran saya menulis program ini:
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
menghasilkan kode asm untuk setiap fungsi sebagai berikut:
kode asm untuk
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
kode asm untuk
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
Anehnya, g(bool)
menghasilkan lebih banyak asm
instruksi! Apakah ini berarti if(bool)
lebih lambat dari if(int)
? Dulu saya pikir bool
dirancang khusus untuk digunakan dalam pernyataan bersyarat seperti if
, jadi saya mengharapkan g(bool)
untuk menghasilkan lebih sedikit instruksi asm, sehingga membuatnya g(bool)
lebih efisien dan cepat.
EDIT:
Saya tidak menggunakan tanda pengoptimalan apa pun untuk sekarang. Tetapi meskipun tidak ada, mengapa itu menghasilkan lebih banyak asm untuk g(bool)
adalah pertanyaan yang saya cari jawaban yang masuk akal. Saya juga harus memberi tahu Anda bahwa -O2
tanda pengoptimalan menghasilkan persis sama dengan asm. Tapi bukan itu pertanyaannya. Pertanyaannya adalah apa yang saya tanyakan.
g(bool)
adalah pertanyaan yang saya cari jawaban yang masuk akal.Jawaban:
Masuk akal bagiku. Kompilator Anda tampaknya mendefinisikan
bool
sebagai nilai 8-bit, dan ABI sistem Anda memerlukannya untuk "mempromosikan" argumen integer kecil (<32-bit) ke 32-bit saat mendorongnya ke tumpukan panggilan. Jadi untuk membandingkan abool
, kompilator menghasilkan kode untuk mengisolasi byte paling signifikan dari argumen 32-bit yang diterima g, dan membandingkannyacmpb
. Dalam contoh pertama,int
argumen menggunakan 32 bit penuh yang didorong ke tumpukan, jadi itu hanya membandingkan keseluruhannya dengancmpl
.sumber
__int64
lebih cepat dariint
? Atau CPU menangani integer 32-bit dengan set instruksi 32-bit secara terpisah?Menyusun dengan
-03
memberikan yang berikut untuk saya:f:
pushl %ebp movl %esp, %ebp cmpl $1, 8(%ebp) popl %ebp sbbl %eax, %eax andb $58, %al addl $99, %eax ret
g:
pushl %ebp movl %esp, %ebp cmpb $1, 8(%ebp) popl %ebp sbbl %eax, %eax andb $58, %al addl $99, %eax ret
.. sehingga mengkompilasi dasarnya kode yang sama, kecuali untuk
cmpl
vscmpb
. Artinya, perbedaannya, jika ada, tidak masalah. Dilihat oleh kode yang tidak dioptimalkan tidak adil.Edit untuk memperjelas maksud saya. Kode yang tidak dioptimalkan adalah untuk debugging sederhana, bukan untuk kecepatan. Membandingkan kecepatan kode yang tidak dioptimalkan tidak masuk akal.
sumber
cmpl
untuk yang satu dancmpb
yang lainnya?bool
adalah satu byte dan anint
adalah empat. Saya tidak berpikir ada yang lebih istimewa dari itu.bool
sebagai tipe 8 bit.char
, yang menurut definisi adalah byte, dan merupakan unit beralamat terkecil.bool
Ukurannya ditentukan oleh implementasi, dan mungkin 1, 4, atau 8, atau apa pun. Namun, penyusun cenderung membuatnya menjadi satu.Ketika saya menyusun ini dengan serangkaian opsi yang waras (khususnya -O3), inilah yang saya dapatkan:
Untuk
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
Untuk
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
Mereka masih menggunakan instruksi yang berbeda untuk perbandingan (
cmpb
untuk boolean vs.cmpl
untuk int), tetapi sebaliknya badannya identik. Sekilas melihat manual Intel memberitahu saya: ... tidak banyak apa-apa. Tidak ada yang namanyacmpb
ataucmpl
di manual Intel. Mereka semuacmp
dan saya tidak dapat menemukan tabel waktu saat ini. Saya menebak, bagaimanapun, bahwa tidak ada perbedaan jam antara membandingkan byte langsung vs membandingkan langsung panjang, jadi untuk semua tujuan praktis kodenya identik.diedit untuk menambahkan yang berikut ini berdasarkan penambahan Anda
Alasan kode berbeda dalam kasus yang tidak dioptimalkan adalah karena kode tidak dioptimalkan. (Ya, ini melingkar, saya tahu.) Ketika kompilator menjalankan AST dan menghasilkan kode secara langsung, ia tidak "tahu" apa pun kecuali apa yang ada di titik langsung AST itu masuk. Pada titik itu ia kekurangan semua informasi kontekstual yang diperlukan untuk mengetahui bahwa pada titik khusus ini, ia dapat memperlakukan tipe yang dideklarasikan
bool
sebagaiint
. Boolean jelas secara default diperlakukan sebagai byte dan ketika memanipulasi byte di dunia Intel Anda harus melakukan hal-hal seperti sign-extends untuk membawanya ke lebar tertentu untuk diletakkan di tumpukan, dll. (Anda tidak dapat mendorong byte .)Namun, ketika pengoptimal melihat AST dan melakukan keajaibannya, pengoptimal melihat konteks sekitarnya dan "mengetahui" kapan dapat mengganti kode dengan sesuatu yang lebih efisien tanpa mengubah semantik. Jadi "tahu" itu bisa menggunakan bilangan bulat dalam parameter dan dengan demikian kehilangan konversi dan pelebaran yang tidak perlu.
sumber
l
danb
sufiks yang digunakan hanya dalam sintaks AT&T. Mereka hanya mengacu pada versicmp
menggunakan operan 4 byte (panjang) dan 1 byte (byte). Jika ada ambiguitas dalam sintaksis intel, secara konvensional operan memori diberi tagBYTE PTR
,WORD PTR
atauDWORD PTR
alih-alih memberi sufiks pada opcode.cmp
memiliki kinerja yang sama, dan tidak ada hukuman register parsial untuk membaca%dil
. (Tapi itu tidak menghentikan clang dari membuat kios register parsial yang lucu dengan menggunakan ukuran byteand
pada AL sebagai bagian dari case-flipping tanpa cabang antara 99 dan -99.)Dengan GCC 4.5 di Linux dan Windows setidaknya,
sizeof(bool) == 1
. Pada x86 dan x86_64, Anda tidak dapat mengirimkan kurang dari nilai register tujuan umum ke suatu fungsi (baik melalui stack atau register tergantung pada konvensi pemanggilan, dll ...).Jadi kode untuk bool, ketika tidak dioptimasi, sebenarnya berjalan cukup panjang untuk mengekstrak nilai bool itu dari tumpukan argumen (menggunakan slot tumpukan lain untuk menyimpan byte itu). Ini lebih rumit daripada hanya menarik variabel berukuran register asli.
sumber
sizeof(bool)
dansizeof(wchar_t)
ditentukan oleh implementasi " . Jadi, ucapansizeof(bool) == 1
tersebut tidak sepenuhnya benar kecuali Anda berbicara tentang versi tertentu dari kompilator tertentu.Pada level mesin tidak ada yang namanya bool
Sangat sedikit arsitektur set instruksi yang mendefinisikan jenis jenis operan boolean apa pun, meskipun sering ada instruksi yang memicu tindakan pada nilai bukan nol. Untuk CPU, biasanya, semuanya adalah salah satu jenis skalar atau rangkaiannya.
Kompiler tertentu dan ABI tertentu perlu memilih ukuran tertentu untuk
int
danbool
jika, seperti dalam kasus Anda, ukuran ini berbeda, mereka mungkin menghasilkan kode yang sedikit berbeda, dan pada beberapa tingkat pengoptimalan, satu mungkin sedikit lebih cepat.Mengapa bool satu byte pada banyak sistem?
Lebih aman memilih
char
tipe untuk bool karena seseorang mungkin membuat array yang sangat besar.Update: dengan "lebih aman", maksud saya: untuk compiler dan pelaksana perpustakaan. Saya tidak mengatakan orang perlu menerapkan ulang tipe sistem.
sumber
bool
diwakili oleh bit; jadi byte akan menjadi pertukaran yang bagus untuk kecepatan / kekompakan data di banyak implementasi.char
daripadabool
" tetapi hanya menggunakan "char
tipe" yang berarti "1 byte" ketika mengacu pada ukuran yang dipilih kompilator untukbool
objek.Ya, diskusi itu menyenangkan. Tapi coba saja:
Kode tes:
#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; }
Dikompilasi pada laptop Ubuntu 10.10 64-bit dengan: g ++ -O3 -o / tmp / test_i /tmp/test_i.cpp
Perbandingan berbasis bilangan bulat:
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
Uji Boolean / cetak tanpa komentar (dan bilangan bulat berkomentar):
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
Mereka sama dengan 1 tugas dan 2 perbandingan setiap loop lebih dari 30 juta loop. Temukan hal lain untuk dioptimalkan. Misalnya, jangan gunakan strcmp jika tidak perlu. ;)
sumber
Ini sebagian besar akan tergantung pada kompiler dan optimasi. Ada diskusi menarik (tanpa bahasa) di sini:
Apakah "if ([bool] == true)" memerlukan satu langkah lebih banyak daripada "if ([bool])"?
Juga, lihat posting ini: http://www.linuxquestions.org/questions/programming-9/c-compiler-handling-of-boolean-variables-290996/
sumber
Mendekati pertanyaan Anda dengan dua cara berbeda:
Jika Anda secara khusus berbicara tentang C ++ atau bahasa pemrograman apa pun yang akan menghasilkan kode assembly dalam hal ini, kami terikat pada kode apa yang akan dihasilkan oleh compiler di ASM. Kami juga terikat dengan representasi benar dan salah di c ++. Integer harus disimpan dalam 32 bit, dan saya cukup menggunakan byte untuk menyimpan ekspresi boolean. Cuplikan ASM untuk pernyataan bersyarat:
Untuk bilangan bulat:
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
Untuk 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
Jadi, itulah mengapa perbandingan kecepatan sangat bergantung pada kompilasi. Dalam kasus di atas, bool akan sedikit lebih cepat karena
cmp
akan mengimplikasikan pengurangan untuk pengaturan flag. Ini juga bertentangan dengan apa yang dihasilkan kompilator Anda.Pendekatan lain, yang jauh lebih sederhana, adalah dengan melihat logika ekspresi itu sendiri dan mencoba untuk tidak khawatir tentang bagaimana kompilator akan menerjemahkan kode Anda, dan saya pikir ini adalah cara berpikir yang jauh lebih sehat. Saya masih percaya, pada akhirnya, bahwa kode yang dihasilkan oleh kompilator sebenarnya mencoba memberikan resolusi yang benar. Yang saya maksud adalah, mungkin jika Anda meningkatkan kasus uji di pernyataan if dan tetap menggunakan boolean di satu sisi dan bilangan bulat di sisi lain, kompilator akan membuatnya sehingga kode yang dihasilkan akan dieksekusi lebih cepat dengan ekspresi boolean di tingkat mesin.
Saya menganggap ini adalah pertanyaan konseptual, jadi saya akan memberikan jawaban konseptual. Diskusi ini mengingatkan saya pada diskusi yang biasa saya lakukan tentang apakah efisiensi kode diterjemahkan menjadi lebih sedikit baris kode dalam perakitan. Tampaknya konsep ini secara umum diterima sebagai benar. Mempertimbangkan bahwa melacak seberapa cepat ALU akan menangani setiap pernyataan tidak memungkinkan, opsi kedua adalah fokus pada lompatan dan perbandingan dalam perakitan. Jika demikian, perbedaan antara pernyataan boolean atau bilangan bulat dalam kode yang Anda sajikan menjadi agak representatif. Hasil ekspresi dalam C ++ akan mengembalikan nilai yang kemudian akan diberi representasi. Di sisi lain, dalam perakitan, lompatan dan perbandingan akan didasarkan pada nilai numerik terlepas dari jenis ekspresi apa yang dievaluasi kembali pada Anda C ++ if pernyataan. Penting pada pertanyaan-pertanyaan ini untuk mengingat bahwa pernyataan logika murni seperti ini berakhir dengan overhead komputasi yang sangat besar, meskipun satu bit akan mampu melakukan hal yang sama.
sumber