2016-07-26 5 views
1

Ich habe eine Bedingung wie die folgenden, wo ich nur die zweite bool der Auslöser für ein einziges Mal haben wollen, da diese Bedingung relativ oft aufgerufen wird, mag ich nicht die Idee, die Zuordnung von es jedes Mal falsch zu tun Die Bedingung ist wahr, also versuchte ich, die Reihenfolge von logischem UND und ODER und den Post-Inkrement-Operator zu nutzen. Aber es scheint zu funktionieren, tu nicht, was ich erwartet habe. Gibt es eine Möglichkeit, einen Post-State-Schalter für diese Leitung zu erstellen?Gibt es eine Möglichkeit, einen "post-switch" -ähnlichen Vorgang mit bool durchzuführen?

wo firstTitleNotSet ist:

bool firstTitleNotSet; 

if (titleChangedSinceLastGet() || (p_firstTitleNotSet && p_firstTitleNotSet++)) 

Die Idee ist, dass der erste Teil der primäre Auslöser ist und der zweite ist der Auslöser, die nur das erste Mal ausgelöst hat.

Während ich leicht

if (titleChangedSinceLastGet() || p_firstTitleNotSet) 
{ 
    firstTitleNotSet = false; 
    //... 
} 

tun könnte Ich mag das nicht, wie es falsch ist Neuzuweisung, wann immer der bedingte Block aufgerufen wird.

So gibt es einen Weg der "post change" der Wert eines bool von true bis false? Ich weiß, dass dies umgekehrt funktionieren würde, aber dies würde den Vorteil der Methode zunichte machen, bei der die meiste Zeit der wahre Auslöser ist und daher die folgende Überprüfung überspringt.

Hinweis: Die Gründe, warum ich solche Überlegungen angestellt habe, sind, anstatt nur den zweiten Fall zu nehmen, dass dieser Block häufig aufgerufen wird, um die verbrauchte Laufzeit zu optimieren.

+0

Haben Sie versucht, etwas wie 'if (std :: exchange (p_firstTitleNotSet, false) == wahr)'? – KABoissonneault

+0

@KABoissonneault: Nein, ich muss zugeben, dass ich es nicht einmal weiß. Aber ich bin mir noch nicht ganz sicher, ob das wirklich funktionieren würde, denn es sieht für mich wie eine Methode aus, die bei jedem Aufruf ausgeführt wird und den Wert umschalten lässt, was absolut nicht das ist, was ich benötige. – dhein

+3

Dies scheint wie eine nutzlose Mikro-Optimierung, wie wahrscheinlich ist es, dass diese einzelne Zuweisung jedes Mal, wenn die Bedingung wahr ist, einen merklichen Unterschied in der Leistung machen wird? – SirGuy

Antwort

2

Nun, könnte man so etwas tun:

if (titleChangedSinceLastGet() || 
    (p_firstTitleNotSet ? ((p_firstTitleNotSet=false), true):false)) 

Eine alternative Syntax wäre:

if (titleChangedSinceLastGet() || 
    (p_firstTitleNotSet && ((p_firstTitleNotSet=false), true))) 

Entweder man sieht etwas hässlich. Beachten Sie jedoch, dass diese NICHT die gleiche wie Ihre andere Alternative ist:

if (titleChangedSinceLastGet() || p_firstTitleNotSet) 
{ 
    p_firstTitleNotSet = false; 
    //... 
} 

Mit Ihrer vorgeschlagenen Alternative, Pontifikat die Tatsache, dass p_firstTitleNotSet-false zurückgesetzt wird, egal was, auch wenn die bedingte eingegeben wurde weil titleChangedSinceLastGet().

+0

Ich bin mir bewusst, dass das letzte nicht genau das gleiche ist wie das andere, aber in meinem Fall würde es sowohl dem geforderten Verhalten entsprechen. Wie auch immer, dein zweiter Block ist EAAXACTLY, was ich versucht habe zu erreichen. Ich stimme dir zu, dass es hässlich ist. Aber gibt es einen anderen Weg, den gleichen Leistungsvorteil von besser lesbarem Code in diesem Fall zu erreichen? – dhein

+0

Wenn Sie das Flag explizit auf "false" innerhalb von "if" zurücksetzen, verwenden Sie das. Ich wäre überrascht, wenn ein Unterschied in der Leistung tatsächlich messbar wäre. Moderne CPUs mit Tiefpipeline sind ziemlich gut darin, was sie tun. –

+0

Können Sie die Syntax erklären oder das im '((p_firstTitleNotSet = false),)' Segment enthaltene Konzept zitieren? Das habe ich noch nie zuvor gesehen. – rtmh

0

, die funktionieren sollte:

int firstTitleSet = 0; 
if (titleChangedSinceLastGet() || (!firstTitleSet++)) 

Wenn Sie einen Überlauf vermeiden möchten, können Sie tun:

int b = 1; 
if (titleChangedSinceLastGet() || (b=b*2%4)) 

bei der ersten Iteration b=2 während b=0 an den Rest von ihnen.

+0

Dies würde das Programm zum Absturz bringen einige Hand voll von Minuten wegen korrekt werden im Falle von Integer-Überlauf (nicht zu erwähnen Integer-Überlauf ist undefined Verhalten), Eine Idee, die ich auch kam, aber die Kontrollen passieren oft. – dhein

+0

@Zaibis Sehen Sie sich die bearbeitete Antwort an. –

+0

Wieder fehlt der Punkt. Es ist wahr, dass dies sich so verhalten würde, wie es erforderlich ist. Aber das macht die Leistung noch schlimmer. Ich meine, ich versuche, eine einzelne boolesche Asignment für jede Ausführung zu optimieren, wo Sie für jede Ausführung 2 Integer-Operationen + 1 Integer-Asignment hinzufügen. Dies ist der Punkt der Optimierung fehlt. Beachten Sie auch, dass "Überlauf vermeiden" sollte, da es sonst UB ist. – dhein

0

In dieser Situation, ich schreibe in der Regel:

bool firstTitleNotSet = true; 

if (titleChangedSinceLastGet() || firstTitleNotSet) 
{ 
    if (firstTileNotSet) firstTitleNotSet = false; 
    //... 
} 

Das zweite Vergleich wird durch den Compiler wahrscheinlich optimiert werden.

Aber wenn Sie haben eine Vorliebe für ein Post-Inkrementoperator:

int iterationCount = 0; 
if (titleChangedSinceLastGet() || iterationCount++ != 0) 
{ 
    //... 
} 

Beachten Sie, dass dies ein Problem sein, wenn iterationCount überläuft, aber das gleiche gilt für die bool firstTitleNotSet ist, dass Sie nach dem Inkrementieren waren.

In Bezug auf Code Lesbarkeit und Wartbarkeit, würde ich die ehemalige empfehlen. Wenn die Logik Ihres Codes solide ist, können Sie sich wahrscheinlich darauf verlassen, dass der Compiler einen sehr guten Job macht, um ihn zu optimieren, auch wenn er für Sie unelegant aussieht.

+0

Nun, dein erstes Beispiel ist das, was ich genau vermeiden will, wenn es nicht noch schlimmer ist, was ich mache, da es Bedingungen hat, die jedes Mal überprüft werden. Und Ihr zweites Beispiel würde das Problem lösen, wenn ich in diesem Fall keinen Integer-Überlauf hätte. – dhein

+0

Ich weiß nicht, ob Ihre Motivation Code-Eleganz oder Laufzeit ist, aber es klingt wie letzteres. Es ist wahrscheinlich, dass Sie versuchen, den Quellcode auf eine Weise zu optimieren, die der Laufzeitleistung wahrscheinlich nicht zuträglich ist. Wenn Sie über diese Detailebene besorgt sind, würde ich empfehlen, dass Sie den Code zerlegen, um zu sehen, was der Compiler tatsächlich generiert. Selbst die Disassemblierung sagt Ihnen nicht genau, was die Leistung in einem modernen Prozessor mit tiefem Pipelining sein wird, aber Sie werden der Grundwahrheit viel näher sein. –

+0

Bewußt, ich war darüber unklar und habe es in einem Schnitt geklärt. Aber beachte auch, dass mein erstes Beispiel, wie Sam Varsh es in seiner Antwort nun abgeschlossen hat, im Falle, dass der Compiler die Sprache korrekt implementiert, definitiv kein so eleganter geschriebener Code ist, sondern per Sprachdefinition nie mehr Operationen als die anderen Fälle und abhängig ist auf die Laufzeit Ergebnisse manchmal noch weniger. Natürlich wird der Compiler es falsch interpretieren oder es ist auf einer anderen Ebene affiziert. Aber das ist nichts, was ich hier zu betrachten versuche. – dhein

1

eine besser lesbare Art und Weise als die Zuordnung in einem ternären Operator innerhalb eines or innerhalb eines if wäre nur die Operationen eigenen Angaben bewegen:

bool needsUpdate = titleChangedSinceLastGet(); 
    if(!needsUpdate && firstTitleSet) 
    { 
     needsUpdate = true; 
     firstTitleSet = false; 
    } 

    if(needsUpdate) 
    { 
     //... 
    } 

Diese wahrscheinlich sehr ähnliche Anordnung als die weniger produzieren lesbare Alternative vorgeschlagen, da ternäre Operatoren meist nur syntaktische Zucker um if Aussagen sind.

Um dies zu demonstrieren, ich GCC Explorer den folgenden Code haben:

extern bool zuerst; Bool geändert();

int f1() 
{ 
    if (changed() || 
    (first ? ((first=false), true):false)) 
    return 1; 
    return 0; 
} 

int f2() 
{ 
    bool b = changed(); 
    if(!b && first) 
    { 
    b = true; 
    first = false; 
    } 
    return b; 
} 

und die generierte Assembly hatte nur geringe Unterschiede in der generierten Assembly nach Optimierungen. Schauen Sie sich selbst an.

Ich behaupte jedoch, dass es sehr unwahrscheinlich ist, einen merklichen Unterschied in der Leistung zu machen, und dass dies mehr Interesse ist.
Meiner Meinung nach:

if(titleChangedSinceLastUpdate() || firstTitleSet) 
    { 
     firstTitleSet = false; 
     //... 
    } 

ist eine (mindestens) ebenso gute Wahl.

Sie können die Zusammenstellung der obigen Funktionen mit dieser vergleichen, um weiter zu vergleichen.

bool f3() 
{ 
    if(changed() || first) 
    { 
    first = false; 
    return true; 
    } 
    return false; 
} 
+0

Nach der Rückmeldung bin ich hier und der Vorteil ist wirklich so klein, ich bleibe bei der Idee, die ich hatte, was dein zweites Beispiel ist. aber dein erstes Beispiel mit einem zusätzlichen Konditional wird immer schlimmer sein, nicht wahr? – dhein

+0

@Zaibis Ich wette dir einen Dollar, dass dies eine sehr ähnliche Assembly zu der "Zuweisung innerhalb einer ternären innerhalb einer' oder 'innerhalb einer 'if'" Methode erzeugt. Aus dem Compiler-Punkt des Unterschieds zwischen einem ternären Operator und einer "if" -Anweisung ist nur, wie es zu analysieren ist. – SirGuy

+0

Wenn du das in deiner Antwort ausarbeiten könntest, würdest du zumindest eine Verbesserung von mir dafür bekommen. – dhein

Verwandte Themen