Hangisi daha hızlı: if (bool) veya if (int)?


95

Hangi değeri kullanmak daha iyidir? Boolean doğru mu, Tamsayı 1 mi?

Yukarıdaki başlık bana bazı deneyler yaptırdı boolve intiçinde ifkoşulu. Ben de meraktan bu programı yazdım:

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 aşağıdaki gibi her işlev için asm kodu üretir:

  • asm kodu 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 kodu 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
    

Şaşırtıcı bir şekilde, g(bool)daha fazla asmtalimat üretir ! Bundan if(bool)biraz daha yavaş olduğu anlamına mı geliyor if(int)? Eskiden boolözellikle koşullu ifadelerde kullanılmak üzere tasarlandığını düşünürdüm if, bu nedenle g(bool)daha az asm komutu üretmeyi bekliyordum , böylece g(bool)daha verimli ve hızlı hale getirmeyi bekliyordum .

DÜZENLE:

Şu an için herhangi bir optimizasyon bayrağı kullanmıyorum. Ama yokluğu bile, neden daha fazla asm üretiyor, çünkü g(bool)mantıklı bir cevap aradığım bir soru. Ayrıca -O2optimizasyon bayrağının tamamen aynı asm ürettiğini söylemeliyim . Ama soru bu değil. Soru sorduğum şey.



32
Ayrıca, makul optimizasyonlar etkinleştirilmiş olarak bunları karşılaştırmadığınız sürece bu adil olmayan bir testtir.
Daniel Pryden

9
@Daniel: İkisiyle de herhangi bir optimizasyon bayrağı kullanmıyorum. Ama yokluğu bile, neden daha fazla asm üretiyor, çünkü g(bool)mantıklı bir cevap aradığım bir soru.
Nawaz

8
Neden sadece programı çalıştırıp sonucu zamanlamakla kalmayıp asm okuma zahmetine giriyorsunuz ? Talimatların sayısı performans hakkında pek bir şey söylemiyor. Sadece komut uzunluklarını değil, aynı zamanda bağımlılıkları ve komut türlerini de hesaba katmanız gerekir (bunlardan bazıları daha yavaş mikrokod yolu kullanılarak çözülür, hangi yürütme birimlerine ihtiyaç duyarlar, talimatın gecikmesi ve verimi nedir? şube? Anı erişimi?
jalf

2
@user bilinmiyor ve @Malvolio: Açıkçası; Bunların hepsini üretim kodu için yapmıyorum. Yazımın başında da bahsettiğim gibi "O yüzden sadece meraktan bu programı yazdım" . Yani evet, tamamen varsayımsal .
Nawaz

3
Bu meşru bir soru. Ya eşdeğerdirler ya da biri daha hızlıdır. ASM büyük olasılıkla yardımcı olma veya yüksek sesle düşünme çabasıyla yayınlanmıştır, bu nedenle soruyu atlatmak ve "sadece okunabilir kod yaz" demek yerine, bilmiyorsanız soruyu veya STFU'yu yanıtlayın. Söyleyecek yararlı hiçbir şeyim yok;) Benim katkım, sorunun yanıtlanabilir olması ve "sadece okunabilir kod yazmanın" sorudan kaçmaktan başka bir şey olmamasıdır.
Triynko

Yanıtlar:


100

Bana mantıklı geldi. Derleyiciniz görünüşe göre a'yı bool8 bitlik bir değer olarak tanımlıyor ve sistem ABI'niz küçük (<32 bit) tamsayı argümanlarını çağrı yığınına iterken 32 bit'e "yükseltmesini" gerektiriyor. Dolayısıyla, a'yı karşılaştırmak booliçin derleyici, g'nin aldığı ve onu karşılaştırdığı 32 bitlik argümanın en az anlamlı baytını izole etmek için kod üretir cmpb. İlk örnekte, intargüman yığına itilen tam 32 biti kullanır, bu nedenle basitçe her şeyle karşılaştırır cmpl.


4
Katılıyorum. Bu, bir değişken türü seçerken, onu potansiyel olarak rekabet eden iki amaç için seçtiğinizi aydınlatmaya yardımcı olur: depolama alanı ve hesaplama performansı.
Triynko

3
Bu aynı zamanda __int64daha hızlı olan 64-bit işlemler için de geçerli intmi? Veya CPU, 32 bitlik komut setleriyle ayrı ayrı 32 bitlik tamsayı mu ele alır?
Crend King

1
@CrendKing belki başka bir soru sormaya değer mi?
Görünen Ad

81

İle derlemek -03bana şunu verir:

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

.. böylece cmplvs hariç, esasen aynı koda derlenir cmpb. Bu, eğer varsa, farkın önemli olmadığı anlamına gelir. Optimize edilmemiş koda göre yargılamak adil değildir.


Düzenleme benim açımdan netleştirmek için. Optimize edilmemiş kod, hız için değil, basit hata ayıklama içindir. Optimize edilmemiş kodun hızını karşılaştırmak anlamsızdır.


8
Kararınıza katılıyorum ama ilginç kısmı atladığınızı düşünüyorum. Neden biri cmpliçin diğeri için kullanıyor cmpb?
jalf

22
@jalf: Çünkü a booltek bir bayttır ve bir intdörttür. Bundan daha özel bir şey olduğunu sanmıyorum.
CB Bailey

7
Diğer yanıtların nedenlere daha fazla dikkat ettiğini düşünüyorum: bunun nedeni, söz konusu platformun bool8 bitlik bir tip olarak işlem görmesidir .
Alexander Gessler

9
@Nathan: Hayır. C ++ 'ın bit veri türleri yoktur. En küçük tür, chartanım gereği bir bayt olan ve adreslenebilir en küçük birimdir. boolboyutu, uygulama tanımlıdır ve 1, 4 veya 8 veya her neyse olabilir. Yine de derleyiciler bunu bir yapma eğilimindedir.
GManNickG

6
@Nathan: Bu Java'da da zor. Java, bir booleanın temsil ettiği verinin bir bitin değeri olduğunu söyler, ancak bu bitin nasıl saklandığı hala uygulama tanımlıdır. Pragmatik bilgisayarlar bitleri ele almazlar.
GManNickG

26

Bunu mantıklı bir dizi seçenekle (özellikle -O3) derlediğimde, işte elde ettiğim şey:

Şunun için 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

Şunun için 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

Karşılaştırma için hala farklı talimatlar kullanıyorlar ( cmpbboole ve cmplint için), ancak aksi takdirde gövdeler aynıdır. Intel kılavuzlarına hızlı bir bakış bana şunu söylüyor: ... pek bir şey değil. Diye bir şey yoktur cmpbveya cmplIntel kılavuzlarında. Hepsi bu cmpve şu anda zamanlama tablolarını bulamıyorum. Bununla birlikte, anlık bir baytı karşılaştırmakla uzun bir anında karşılaştırmak arasında saat farkı olmadığını tahmin ediyorum, bu nedenle tüm pratik amaçlar için kod aynıdır.


eklemenize göre aşağıdakileri eklemek için düzenlendi

Optimize edilmemiş durumda kodun farklı olmasının nedeni, optimize edilmemiş olmasıdır. (Evet, döngüsel, biliyorum.) Derleyici AST'yi yürüdüğünde ve doğrudan kod ürettiğinde, içinde bulunduğu AST'nin en yakın noktasında olanlar dışında hiçbir şey "bilmiyor". Bu noktada, gereken tüm bağlamsal bilgilerden yoksundur. bu belirli noktada bildirilen türü boolbir int. Bir boole, açık bir şekilde varsayılan olarak bir bayt olarak kabul edilir ve Intel dünyasında baytları işlerken, onu yığına koymak için belirli genişliklere getirmek için işaret uzatma gibi şeyler yapmanız gerekir. (Bir baytı itemezsiniz. .)

İyileştirici AST'yi görüntülediğinde ve sihrini yaptığında, bununla birlikte, çevredeki bağlama bakar ve semantiği değiştirmeden kodu ne zaman daha verimli bir şeyle değiştirebileceğini "bilir". Bu nedenle, parametrede bir tamsayı kullanabileceğini "bilir" ve böylece gereksiz dönüştürmeleri ve genişletmeyi kaybedebilir.


1
Haha, derleyicinin basitçe 99 veya 99 + 58 = 157 = -99 (işaretli 8 bit taşması) döndürmesini seviyorum ... ilginç.
deceleratedcaviar

@Daniel: Ben bile sevdim. İlk başta "-99 nerede" dedim ve hemen bunun çok garip bir şey yaptığını anladım.
Nawaz

7
lve byalnızca AT&T sözdiziminde kullanılan soneklerdir. cmpSırasıyla 4 bayt (uzun) ve 1 bayt (bayt) işlenenleri kullanma sürümlerine başvururlar . Intel sözdiziminde bir belirsizlik olduğu yerde, geleneksel olarak bellek işlenen ile etiketlenir BYTE PTR, WORD PTRya da DWORD PTRbunun yerine işlem kodu ile ilgili bir son ek koyarak.
CB Bailey

Zamanlama tabloları: agner.org/optimize Her iki işlenen boyutu cmpaynı performansa sahiptir ve okuma için kısmi kayıt cezaları yoktur %dil. (Ancak bu, and99 ile -99 arasında dallanmadan büyük / küçük harf çevirme işleminin bir parçası olarak AL üzerinde bayt boyutunu kullanarak eğlenceli bir şekilde kısmi kayıt duraklaması oluşturmasını engellemez .)
Peter Cordes

13

En azından Linux ve Windows üzerinde GCC 4.5 ile sizeof(bool) == 1. X86 ve x86_64'te, genel amaçlı bir kaydın değerinden daha azını bir işleve aktaramazsınız (çağrı kuralına bağlı olarak yığın veya kayıt yoluyla vb.).

Dolayısıyla, bool için kod, optimize edilmediğinde, aslında bool değerini argüman yığınından çıkarmak için biraz uzar (bu baytı kaydetmek için başka bir yığın yuvası kullanarak). Bu, yerel yazmaç boyutlu bir değişkeni çekmekten daha karmaşıktır.


C ++ 03 standardından, §5.3.3 / 1: " sizeof(bool)ve sizeof(wchar_t)uygulama tanımlıdır. " Bu nedenle sizeof(bool) == 1, belirli bir derleyicinin belirli bir sürümünden bahsetmediğiniz sürece söylemek kesinlikle doğru değildir.
ildjarn

9

Makine seviyesinde bool diye bir şey yoktur

Sıfır olmayan değerler üzerinde bir eylemi tetikleyen komutlar olsa da, çok az komut kümesi mimarisi herhangi bir tür boole işlenen türünü tanımlar. CPU için genellikle her şey skaler türlerden biri veya bunların bir dizisidir.

Belirli derleyici ve belirli bir ABI özgü boyutları seçmek gerekir intve boolve sizin durumunuzda olduğu gibi, bu da biraz farklı kod oluşturabilir ve optimizasyon birinin bazı seviyelerinde biraz daha hızlı olabilir farklı boyutlarda olduğunda,.

Bool neden birçok sistemde bir bayttır?

charBool için bir tür seçmek daha güvenlidir çünkü birileri gerçekten geniş bir dizi oluşturabilir.

Güncelleme: tarafından "güvenli", yani: derleyici ve kitaplık implementors. İnsanların sistem türünü yeniden uygulaması gerektiğini söylemiyorum.


2
+1 Eğer boolbitlerle temsil edildiyse x86'daki ek yükü düşünün ; bu nedenle bayt, birçok uygulamada hız / veri kompaktlığı için güzel bir değiş tokuş olacaktır.
hardmath

1
@Billy: Sanırım " charyerine kullan" demiyordu, bunun boolyerine charderleyicinin boolnesneler için seçtiği boyuta atıfta bulunurken "1 bayt" anlamında "tür" kullandı .
Dennis Zickefoose

Elbette, her programın seçmesi gerektiğini kastetmedim, sadece sistem bool türünün neden 1 bayt olduğuna dair bir mantık geliştiriyordum.
DigitalRoss

@Dennis: Ah, mantıklı.
Billy ONeal

7

Evet, tartışma eğlenceli. Ama sadece test edin:

Test kodu:

#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 bit Ubuntu 10.10 dizüstü bilgisayarda şu şekilde derlenmiştir: g ++ -O3 -o / tmp / test_i /tmp/test_i.cpp

Tam sayı tabanlı karşılaştırma:

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

Boole testi / açıklamasız yazdırma (ve açıklamalı tam sayı):

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 atama ve her döngüde 30 milyon döngü üzerinde 2 karşılaştırma ile aynıdır. Optimize edecek başka bir şey bulun. Örneğin, strcmp'yi gereksiz yere kullanmayın. ;)



0

Sorunuza iki farklı yoldan yaklaşmak:

Özellikle C ++ veya bu konuda derleme kodu üretecek herhangi bir programlama dilinden bahsediyorsanız, derleyicinin ASM'de hangi kodu üreteceğine bağlıyız. Ayrıca c ++ 'da doğru ve yanlışın temsiline bağlıyız. Bir tamsayının 32 bitte saklanması gerekecek ve basitçe boole ifadesini saklamak için bir bayt kullanabilirim. Koşullu ifadeler için asm parçacıkları:

Tam sayı için:

  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 için:

  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

Bu nedenle, hız karşılaştırması bu kadar derlemeye bağımlıdır. Yukarıdaki durumda bool cmp, bayrakların ayarlanması için bir çıkarma anlamına geleceği için biraz hızlı olacaktır. Ayrıca derleyicinizin ürettiği ile de çelişir.

Çok daha basit olan başka bir yaklaşım, ifadenin mantığına kendi başına bakmak ve derleyicinin kodunuzu nasıl çevireceği konusunda endişelenmemektir ve bence bu çok daha sağlıklı bir düşünme şekli. Nihayetinde derleyici tarafından üretilen kodun aslında doğru bir çözüm vermeye çalıştığına inanıyorum. Demek istediğim, eğer if ifadesindeki test durumlarını arttırırsanız ve bir tarafta boole ve diğerinde tamsayı tutarsanız, derleyici bunu yapacaktır, böylece üretilen kod makine seviyesinde boole ifadeleri ile daha hızlı çalışacaktır.

Bunun kavramsal bir soru olduğunu düşünüyorum, bu yüzden kavramsal bir cevap vereceğim. Bu tartışma bana, kod verimliliğinin montajda daha az kod satırı anlamına gelip gelmeyeceği hakkında sıkça yaptığım tartışmaları hatırlatıyor. Görünüşe göre bu kavram genel olarak doğru kabul ediliyor. ALU'nun her bir ifadeyi ne kadar hızlı işleyeceğini takip etmenin uygun olmadığı düşünüldüğünde, ikinci seçenek montajdaki sıçramalara ve karşılaştırmalara odaklanmak olacaktır. Böyle bir durumda, sunduğunuz koddaki boole ifadeleri veya tamsayılar arasındaki fark oldukça temsili hale gelir. C ++ 'daki bir ifadenin sonucu, daha sonra bir temsil verilecek bir değer döndürür. Montajda ise, atlamalar ve karşılaştırmalar, C ++ if ifadenizde ne tür bir ifade değerlendiriliyor olursa olsun sayısal değerlere dayanacaktır. Bu sorularda, bunlar gibi tamamen mantıksal ifadelerin, tek bir bitin aynı şeyi yapabilecek olmasına rağmen, büyük bir hesaplama yükü ile sonuçlandığını hatırlamak önemlidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.