Dereferenzieren oder locale temp Variable?



  • Hallo,

    ich hätte mal eine Frage was günstiger ist wenn ich in einer Schleife sehr oft auf den gleichen Iterator zugreifen will: Jedes mal dereferenzieren oder eine lokale, temporäre Variable erzeugen?

    Also so:

    for (std::string::iterator i = longString.begin(), e = longString.end(); i != e; ++i) {
        // Sehr viele solche Vergleiche in einem Schleifendurchlauf
        if ((*i) == 'a') {
    
        }
    }
    

    oder lieber so:

    for (std::string::iterator i = longString.begin(), e = longString.end(); i != e; ++i) {
        char c_temp = *i;
    
        // Sehr viele solche Vergleiche in einem Schleifendurchlauf
        if (c_temp == 'a') {
    
        }
    }
    

    Was ist hier besser bzw kostet das dereferenzieren überhaupt was? Und wenn, lohnt sich das per temporärer Variable oder ist der Unterschied (bei built-in Typen) vernachlässigbar? Wenn ja, auch bei nicht built-in Typen?

    Schöne Grüße,
    hs



  • for ( const auto& meinchar : longString ) {
        if ( meinchar == 'a' )
    

  • Mod

    Jedes mal dereferenzieren oder eine lokale, temporäre Variable erzeugen?

    Naja, der dereferenzierte Wert ist wahrscheinlich im Cache. Trotzdem könnte eine lokale Variable schneller sein.

    Übrigens solltest du, wenn möglich, range-based for verwenden (wie mein Vorposter demonstriert hat). Aber nicht als Const-Referenz - es sei denn, du nimmst die Adresse.



  • Die Variante mit c_temp ist schneller. Der Compiler ist normalerweise sehr schlau und könnte problemlos ein c_temp einführen, aber manchmal geht das nicht so einfach, weil es semantische Unterschiede geben könnte:

    for (std::string::iterator i = longString.begin(), e = longString.end(); i != e; ++i) {
        do_sth(*it);
        f(); // *it könnte sich ändern
        do_sth_else(*it); // neue Dereferenzierung
    }
    

    vs.

    for (std::string::iterator i = longString.begin(), e = longString.end(); i != e; ++i) {
        char c_temp = *it;
        do_sth(c_temp);
        f(); // c_temp kann sich nicht ändern
        do_sth_else(c_temp); // alles im Register
    }
    

    Arcoth schrieb:

    Naja, der dereferenzierte Wert ist wahrscheinlich im Cache. Trotzdem könnte eine lokale Variable schneller sein.

    😕



  • Arcoth schrieb:

    Übrigens solltest du, wenn möglich, range-based for verwenden (wie mein Vorposter demonstriert hat). Aber nicht als Const-Referenz - es sei denn, du nimmst die Adresse.

    Meiner (vorläufigen) Meinung nach sollte man immer const auto& für range based foor loops verwenden, es ein denn man hat sich gut überlegt, warum es in der konkreten Situation besser ist, etwas anderes zu tun. Nur mit auto wird eine Kopie angelegt, und das ist bei fast allen nicht primitiven Datentypen teuer.



  • manni66 schrieb:

    for ( const auto& meinchar : longString ) {
        if ( meinchar == 'a' )
    

    Aber mit dem auto kann ich doch nicht nen Index weiter "schalten" oder doch?

    Also sowas:

    for (std::string::iterator i = longString.begin(), e = longString.end(); i != e; ++i) {
        char curr = *i, prev = *(i - 1), next = *(i + 1);
    }
    

    Zumindest scheint der wenn ich in der oberen for-Schleife auf meinchar nen Wert drauf addiere einfach den nächst-höheren ASCII Wert zu nehmen und nicht nen Index weiter zu springen...



  • defferenz schrieb:

    for (std::string::iterator i = longString.begin(), e = longString.end(); i != e; ++i) {
        char c_temp = *it;
        do_sth(c_temp);
        f(); // c_temp kann sich nicht ändern
        do_sth_else(c_temp); // alles im Register
    }
    

    Wieso soll sich c_temp nicht ändern können? Wenn der Compiler beweisen kann, dass f nicht auf c_temp zugreift, dann kann er auch beweisen, dass f nicht auf longstring zugreift. Wenn er in f nicht reinkucken kann, dann muss er davon ausgehen, dass jede Memory Location gelesen und geschrieben wird, inklusive c_temp.
    Außerdem wäre es auch egal, weil Funktionsaufrufe generell die Register invalidieren. Selbst wenn der Compiler beweisen kann, dass c_temp in f nicht angefasst wird muss er trotzdem vor dem Aufruf von f in den Speicher von c_temp schreiben und nach f wieder lesen.

    Die einzige Chance ist zu beweisen, dass man die Aufrufe von f und do_sth_else tauschen kann, ohne an der Bedeutung des Programms etwas zu ändern. Und dabei hilft c_temp nicht.



  • happystudent schrieb:

    manni66 schrieb:

    for ( const auto& meinchar : longString ) {
        if ( meinchar == 'a' )
    

    Aber mit dem auto kann ich doch nicht nen Index weiter "schalten" oder doch?

    Nein



  • manni66 schrieb:

    Nein

    Ok, dann fällt die auto Variante hier sowieso weg für mich, da ich diese Funktionalität brauche.

    Dann nehmen ich ab jetzt immer temp Variablen wenn ich mehr als einmal dereferenziere.

    Danke für die Antworten 🙂



  • nwp3 schrieb:

    defferenz schrieb:

    for (std::string::iterator i = longString.begin(), e = longString.end(); i != e; ++i) {
        char c_temp = *it;
        do_sth(c_temp);
        f(); // c_temp kann sich nicht ändern
        do_sth_else(c_temp); // alles im Register
    }
    

    Wieso soll sich c_temp nicht ändern können?

    Weil es eine lokale Variable ist und man auf die von aussen nicht zugreifen kann.

    Wenn der Compiler beweisen kann, dass f nicht auf c_temp zugreift, dann kann er auch beweisen, dass f nicht auf longstring zugreift.

    longString hat mit new allozierten Speicher, mit dem kann alles passieren. Z.B. könntest du einen eigenen new-Handler einsetzen, der immer wieder den Inhalt
    überschreibt.

    Wenn er in f nicht reinkucken kann, dann muss er davon ausgehen, dass jede Memory Location gelesen und geschrieben wird, inklusive c_temp.

    Nein, c_temp kann nicht gelesen und beschrieben werden.

    Außerdem wäre es auch egal, weil Funktionsaufrufe generell die Register invalidieren.

    Nichts wird da invalidiert.

    Selbst wenn der Compiler beweisen kann, dass c_temp in f nicht angefasst wird muss er trotzdem vor dem Aufruf von f in den Speicher von c_temp schreiben und nach f wieder lesen.

    Es macht von der Geschwindigkeit her einen Unterschied, ob vom Stack oder vom Heap gelesen wird.

    Die einzige Chance ist zu beweisen, dass man die Aufrufe von f und do_sth_else tauschen kann, ohne an der Bedeutung des Programms etwas zu ändern. Und dabei hilft c_temp nicht.

    Nö. f kann globale Variablen schreiben, die do_sth_else lesen kann.



  • defferenz schrieb:

    nwp3 schrieb:

    Wieso soll sich c_temp nicht ändern können?

    Weil es eine lokale Variable ist und man auf die von aussen nicht zugreifen kann.

    Wieso nicht? Mit ein bisschen Stack-unwinding geht das. Außerdem kann do_sth ein char & nehmen, davon die Adresse nehmen, diese in eine globale Variable schreiben, welche von f dereferenziert und verändert wird.

    defferenz schrieb:

    longString hat mit new allozierten Speicher, mit dem kann alles passieren. Z.B. könntest du einen eigenen new-Handler einsetzen, der immer wieder den Inhalt
    überschreibt.

    Man kann auch den Stackpointer auf den Heap zeigen lassen, da gibt es gar keinen Unterschied in der Veränderlichkeit.

    defferenz schrieb:

    Wenn er in f nicht reinkucken kann, dann muss er davon ausgehen, dass jede Memory Location gelesen und geschrieben wird, inklusive c_temp.

    Nein, c_temp kann nicht gelesen und beschrieben werden.

    Und wer oder was hindert mich daran? Man kann in C++ beliebige Speicheradressen zu lesen und schreiben versuchen. Und es ist nicht gerade schwierig für f die Adresse von c_temp zu erraten.

    defferenz schrieb:

    Außerdem wäre es auch egal, weil Funktionsaufrufe generell die Register invalidieren.

    Nichts wird da invalidiert.

    Wie soll denn f funktionieren? Register benutzten kann es ja deiner Meinung nach nicht. Oder meinst du f wird alle Register auf den Stack speichern, sein Ding machen und dann den Zustand aller Register wiederherstellen?

    defferenz schrieb:

    Selbst wenn der Compiler beweisen kann, dass c_temp in f nicht angefasst wird muss er trotzdem vor dem Aufruf von f in den Speicher von c_temp schreiben und nach f wieder lesen.

    Es macht von der Geschwindigkeit her einen Unterschied, ob vom Stack oder vom Heap gelesen wird.

    Habe ich doch gar nicht behauptet. Ich habe behauptet, dass c_temp nicht im Register gehalten werden kann, sondern vor Aufruf von f in den Speicher geschrieben werden muss und nach f wieder aus dem Speicher gelesen werden muss. Mit Heap und Stack und Geschwindigkeit hat das garnichts zu tun.

    defferenz schrieb:

    Die einzige Chance ist zu beweisen, dass man die Aufrufe von f und do_sth_else tauschen kann, ohne an der Bedeutung des Programms etwas zu ändern. Und dabei hilft c_temp nicht.

    Nö. f kann globale Variablen schreiben, die do_sth_else lesen kann.

    😕 Irgendwie hat deine Aussage mit meiner nichts zu tun.



  • nwp3 schrieb:

    defferenz schrieb:

    Wenn er in f nicht reinkucken kann, dann muss er davon ausgehen, dass jede Memory Location gelesen und geschrieben wird, inklusive c_temp.

    Nein, c_temp kann nicht gelesen und beschrieben werden.

    Und wer oder was hindert mich daran? Man kann in C++ beliebige Speicheradressen zu lesen und schreiben versuchen. Und es ist nicht gerade schwierig für f die Adresse von c_temp zu erraten.

    Das ist aber oft UB, also darf der Compiler sehr wohl optimieren. Nur dann, wenn irgendwelche Werte per Referenz oder pointer übergeben werden, muss die Addresse per Standard auch die selbe sein.



  • Ich erinnere mich was gelesen zu haben, wo steht, dass man für einen Funktionsaufruf, in den der Compiler nicht reinsehen kann, annehmen muss, dass er jede Memory Location liest und schreibt. Ich finde es aber nicht mehr 😞



  • nwp3 schrieb:

    Ich erinnere mich was gelesen zu haben, wo steht, dass man für einen Funktionsaufruf, in den der Compiler nicht reinsehen kann, annehmen muss, dass er jede Memory Location liest und schreibt. Ich finde es aber nicht mehr 😞

    Ja, das gilt aber nur für veränderbare und auf standartkonformen Wegen erreichbare Variablen.


Log in to reply