Gibt es hier Performance Unterschiede?



  • Da ich kein Fan von aneinandergeketteten Abfragen bin schreibe ich lieber

    A

    if (board.black_queens >= 1) {
      if (black_king_attacker_n >= 2) {
        if (material_item->black_non_pawn_material >= VALUE_QUEEN_O) {
          if (black_king_adjacent_zone_attack_n) {
            // ...
    

    anstatt

    B

    if (board.black_queens >= 1 &&
        black_king_attacker_n >= 2 &&
        material_item->black_non_pawn_material >= VALUE_QUEEN_O &&
        black_king_adjacent_zone_attack_n &&
        // ...
    

    Ist das dasselbe für den Compiler, oder gibt es Performance-Unterschiede? Ich kann das nur schwierig testen, da meine gesamten Sources wie in A angelegt sind.

    Und was ist eurer Meinung nach besser ?

    Danke schonmal für eure Statements 🙂



  • Es ist nicht dasselbe für den compiler, moderne compiler machen aber daraus den selben code.

    Solche verschachtelten Bedingungsblöcke sind Unsinn. Setze anstatt dessen einfach die boolschen Operatoren an eine konstante Tab-Stelle, damit die Bedingunsliste übersichtlicher aussieht.



  • Solche verschachtelten Bedingungsblöcke sind prima.



  • Ich würde sagen B kann nicht schlechter sein als A. Beim "&&", auch "short circuit AND" genannt, wird der zweite Operand nur auswertet, wenn der erste Operand true ist. Aber A könnte langsamer sein als B. Nicht vielleicht in Deinem Fall, aber wenn man zB Intervalle testet:

    int foo(int x)
    {
      if (1 <= x && x <= 3) return 23;
      return 42;
    }
    
    int bar(int x)
    {
      if (1 <= x)
        if (x <= 3) return 23;
      return 42;
    }
    

    Im ersten Fall könnte ein schlauer Compiler den Test 1<=x&&x<=3 zu etwas wie unsigned(i-1)<=2 umbauen (eine Verzweigung gespart). Im zweiten Fall halte ich das nicht mehr so ganz für wahrscheinlich, dass ein Compiler diese Optimierung auch durchfuhren kann.

    Wenn Du's genau wissen willst, kannst Du Dir ja mal den generierten Assemblercode anschauen.

    Edit: Hier ist der Assembler code (g++ version 3.4.5 auf x86 mit -O3)

    00000000 <__Z3fooi>:
       0:   55                      push   %ebp
       1:   89 e5                   mov    %esp,%ebp
       3:   8b 45 08                mov    0x8(%ebp),%eax
       6:   5d                      pop    %ebp
       7:   48                      dec    %eax
       8:   83 f8 03                cmp    $0x3,%eax
       b:   19 c0                   sbb    %eax,%eax
       d:   83 e0 ed                and    $0xffffffed,%eax
      10:   83 c0 2a                add    $0x2a,%eax
      13:   c3                      ret
      14:   8d b6 00 00 00 00       lea    0x0(%esi),%esi
      1a:   8d bf 00 00 00 00       lea    0x0(%edi),%edi
    
    00000020 <__Z3bari>:
      20:   55                      push   %ebp
      21:   89 e5                   mov    %esp,%ebp
      23:   8b 55 08                mov    0x8(%ebp),%edx
      26:   85 d2                   test   %edx,%edx
      28:   7e 0a                   jle    34 <__Z3bari+0x14>
      2a:   83 fa 03                cmp    $0x3,%edx
      2d:   b8 17 00 00 00          mov    $0x17,%eax
      32:   7e 05                   jle    39 <__Z3bari+0x19>
      34:   b8 2a 00 00 00          mov    $0x2a,%eax
      39:   5d                      pop    %ebp
      3a:   c3                      ret
      3b:   90                      nop
    

    Das erste sieht mir irgendwie effizienter aus, da keine bedingten Sprünge.

    Gruß,
    SP



  • volkard schrieb:

    Solche verschachtelten Bedingungsblöcke sind prima.

    Finde ich nicht:

    if (board.black_queens >= 1) {
      if (black_king_attacker_n >= 2) {
        if (material_item->black_non_pawn_material >= VALUE_QUEEN_O) {
          if (black_king_adjacent_zone_attack_n) {
            ... Anweisungen
          }
        }
      }
    }
    
    if( board.black_queens >= 1                                   &&
        black_king_attacker_n >= 2                                &&
        material_item->black_non_pawn_material >= VALUE_QUEEN_O   &&
        black_king_adjacent_zone_attack_n )
    {
       ... Anweisungen
    }
    


  • Ja, ich habs wiedermal komplett verpeilt. Wenn der Code komplizierter wird, ist auf jeden Fall Deine Version viel lesbarer, weil man den Ausdruck nicht inhaltlich zerlegen muß, sondern das einfach grafisch machen kann. Durch die Eingerückten &&-Zeichen ist sofort die totale Übersicht da.

    bool datumIstGueltig(int tag,int monat,int jahr)
    {
       if( monat>0                          &&
           monat<13                         &&
           tag>0                            &&
           tag<32                           &&
           (tag<31                      ¦¦
           (monat>7!=monat%2==1))           &&
           (monat!=2                    ¦¦
           (tag<30                          &&
           (((jahr%4==0                     &&
           jahr%100!=0)                 ¦¦
           jahr%400==0)                 ¦¦
           tag<29)))) 
           return true; 
       else 
          return false;
    }
    

    edit: Übersicht drastisch weiter verbessert, indem || auch einen Platz bekommen hat. Evtl sollte man für Vergleichsoperatoren auch einen Platz einräumen.



  • (A) ist auch angenehmer zu debuggen.
    ich verwende trotzdem meist (B).



  • Ich finde B auch besser, weil es imo mehr das aussagt, was du sagen willst. Du willst, dass etwas UND etwas UND etwas nochmal anderes gleichezeitig wahr sind. Klar versteht man es mit if's auch, aber mit den Verknüpfungen finde ich es intuitiver.



  • drakon schrieb:

    Ich finde B auch besser, weil es imo mehr das aussagt, was du sagen willst. Du willst, dass etwas UND etwas UND etwas nochmal anderes gleichezeitig wahr sind. Klar versteht man es mit if's auch, aber mit den Verknüpfungen finde ich es intuitiver.

    wieso sagt ihr das alle so absolut?
    ich finds idR auch schöner, logische operatoren statt 100 ifs zu benutzen, aber volkards bsp find ich z.bsp. mehr als unleserlich - aber das würd ich eh anders schreiben:

    bool datumIstGueltig(int tag,int monat,int jahr)
    {                  /*0...32, 0...11, 0...INT_MAX*/
      if(tag < 0)
        return false;
    
      switch(monat)
      {
        case 0: //jan
        case 2: //mrz
        case 4: //mai
        case 6: //jul
        case 7: //aug
        case 9: //oct
        case 11: //dez
          return tag < 32;
    
        case 3: //apr
        case 5: //jun
        case 8: //sep
        case 10: //nov
          return tag < 31;
    
        case 1: //feb
          if((jahr%4 == 0) && (jahr%400 == 0))
            return tag < 29;
          return < 28;
    
        default:
          return false;
       }
    }
    

    bb



  • unskilled schrieb:

    wieso sagt ihr das alle so absolut?

    Was war bei meiner Aussage absolut? - Ich habe gesagt, dass ich es (dort, ok habe ich nicht explizit gesagt..) intuitiver finde. Klar gibt es Fälle, wo es anderst rum besser verständlich ist.

    Und auf volkards Beispiel habe ich mich gar nicht bezogen. - Deine Lösung gefällt mir da auch besser. 😉


Anmelden zum Antworten