it-swarm.com.de

Muss ich mich in C ++ um das Zwischenspeichern von Variablen kümmern oder den Compiler die Optimierung überlassen? (Aliasing)

Betrachten Sie den folgenden Code (p ist vom Typ unsigned char* und bitmap->width hat einen ganzzahligen Typ, der genau unbekannt ist und von der Version einer externen Bibliothek abhängt, die wir verwenden.):

for (unsigned x = 0;  x < static_cast<unsigned>(bitmap->width);  ++x)
{
    *p++ = 0xAA;
    *p++ = 0xBB;
    *p++ = 0xCC;
}

Lohnt es sich, es zu optimieren [..]

Könnte es einen Fall geben, in dem dies zu effizienteren Ergebnissen führen könnte, wenn Sie Folgendes schreiben:

unsigned width(static_cast<unsigned>(bitmap->width));
for (unsigned x = 0;  x < width;  ++x)
{
    *p++ = 0xAA;
    *p++ = 0xBB;
    *p++ = 0xCC;
}

... oder ist das für den Compiler trivial zu optimieren?

Was halten Sie für "besseren" Code?

Anmerkung des Herausgebers (Ike): Für diejenigen, die sich über den Streik-Text wundern, war die ursprüngliche Frage, wie formuliert, gefährlich nahe am Off-Topic-Territorium und stand trotz positiver Rückmeldungen kurz davor, geschlossen zu werden Bitte bestrafen Sie jedoch nicht die Antwortenden, die sich mit diesen betroffenen Abschnitten der Frage befasst haben.

114
Yaron Cohen-Tal

Auf den ersten Blick dachte ich, der Compiler könnte für beide Versionen eine entsprechende Assembly mit aktivierten Optimierungsflags generieren. Als ich es überprüfte, war ich überrascht, das Ergebnis zu sehen:

Quelle unoptimized.cpp

hinweis: Dieser Code ist nicht zur Ausführung vorgesehen.

struct bitmap_t
{
    long long width;
} bitmap;

int main(int argc, char** argv)
{
    for (unsigned x = 0 ; x < static_cast<unsigned>(bitmap.width) ; ++x)
    {
        argv[x][0] = '\0';
    }
    return 0;
}

Quelle optimized.cpp

hinweis: Dieser Code ist nicht zur Ausführung vorgesehen.

struct bitmap_t
{
    long long width;
} bitmap;

int main(int argc, char** argv)
{
    const unsigned width = static_cast<unsigned>(bitmap.width);
    for (unsigned x = 0 ; x < width ; ++x)
    {
        argv[x][0] = '\0';
    }
    return 0;
}

Zusammenstellung

  • $ g++ -s -O3 unoptimized.cpp
  • $ g++ -s -O3 optimized.cpp

Montage (nicht optimiert. S)

    .file   "unoptimized.cpp"
    .text
    .p2align 4,,15
.globl main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    .cfi_personality 0x3,__gxx_personality_v0
    movl    bitmap(%rip), %eax
    testl   %eax, %eax
    je  .L2
    xorl    %eax, %eax
    .p2align 4,,10
    .p2align 3
.L3:
    mov %eax, %edx
    addl    $1, %eax
    movq    (%rsi,%rdx,8), %rdx
    movb    $0, (%rdx)
    cmpl    bitmap(%rip), %eax
    jb  .L3
.L2:
    xorl    %eax, %eax
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
.globl bitmap
    .bss
    .align 8
    .type   bitmap, @object
    .size   bitmap, 8
bitmap:
    .zero   8
    .ident  "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-16)"
    .section    .note.GNU-stack,"",@progbits

Montage (optimiert.s)

    .file   "optimized.cpp"
    .text
    .p2align 4,,15
.globl main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    .cfi_personality 0x3,__gxx_personality_v0
    movl    bitmap(%rip), %eax
    testl   %eax, %eax
    je  .L2
    subl    $1, %eax
    leaq    8(,%rax,8), %rcx
    xorl    %eax, %eax
    .p2align 4,,10
    .p2align 3
.L3:
    movq    (%rsi,%rax), %rdx
    addq    $8, %rax
    cmpq    %rcx, %rax
    movb    $0, (%rdx)
    jne .L3
.L2:
    xorl    %eax, %eax
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
.globl bitmap
    .bss
    .align 8
    .type   bitmap, @object
    .size   bitmap, 8
bitmap:
    .zero   8
    .ident  "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-16)"
    .section    .note.GNU-stack,"",@progbits

diff

$ diff -uN unoptimized.s optimized.s
--- unoptimized.s   2015-11-24 16:11:55.837922223 +0000
+++ optimized.s 2015-11-24 16:12:02.628922941 +0000
@@ -1,4 +1,4 @@
-   .file   "unoptimized.cpp"
+   .file   "optimized.cpp"
    .text
    .p2align 4,,15
 .globl main
@@ -10,16 +10,17 @@
    movl    bitmap(%rip), %eax
    testl   %eax, %eax
    je  .L2
+   subl    $1, %eax
+   leaq    8(,%rax,8), %rcx
    xorl    %eax, %eax
    .p2align 4,,10
    .p2align 3
 .L3:
-   mov %eax, %edx
-   addl    $1, %eax
-   movq    (%rsi,%rdx,8), %rdx
+   movq    (%rsi,%rax), %rdx
+   addq    $8, %rax
+   cmpq    %rcx, %rax
    movb    $0, (%rdx)
-   cmpl    bitmap(%rip), %eax
-   jb  .L3
+   jne .L3
 .L2:
    xorl    %eax, %eax
    ret

Die generierte Assembly für die optimierte Version lädt ( lea ) die width -Konstante im Gegensatz zur nicht optimierten Version, die den width -Offset bei jeder Iteration berechnet ( movq ).

Wenn ich Zeit habe, poste ich irgendwann einen Benchmark. Gute Frage.

81
YSC

Tatsächlich enthält Ihr Code-Snippet nicht genügend Informationen, um dies beurteilen zu können, und mir fällt nur Aliasing ein. Aus unserer Sicht ist es ziemlich klar, dass Sie nicht möchten, dass p und bitmap auf dieselbe Position im Speicher verweisen, aber der Compiler weiß das nicht und (weil p ist vom Typ char*) Der Compiler muss dafür sorgen, dass dieser Code auch dann funktioniert, wenn sich p und bitmap überlappen.

Dies bedeutet in diesem Fall, dass, wenn sich die Schleife ändert, bitmap->width durch den Zeiger p dann muss das beim erneuten Lesen gesehen werden bitmap->width später, was wiederum bedeutet, dass das Speichern in einer lokalen Variablen illegal wäre.

Abgesehen davon glaube ich, dass einige Compiler manchmal tatsächlich zwei Versionen desselben Codes generieren werden (ich habe Indizien dafür gesehen, aber nie direkt Informationen darüber gesucht, was der Compiler in diesem Fall tut), und überprüfe schnell, ob die Zeiger vorhanden sind Alias ​​und führen Sie den schnelleren Code aus, wenn dies für Sie in Ordnung ist.

Abgesehen davon halte ich an meiner Bemerkung fest, die Leistung der beiden Versionen einfach zu messen. Mein Geld ist es, keinen konsistenten Leistungsunterschied zwischen den beiden Versionen des Codes zu sehen.

Meiner Meinung nach sind Fragen wie diese in Ordnung, wenn Sie sich mit Theorien und Techniken der Compileroptimierung befassen möchten, aber es ist Zeitverschwendung (eine nutzlose Mikrooptimierung), wenn Sie hier das Ziel haben, das Programm schneller laufen zu lassen.

38
SirGuy

Andere Antworten haben darauf hingewiesen, dass das Herausziehen der Zeigeroperation aus der Schleife das definierte Verhalten aufgrund von Aliasing-Regeln ändern kann, die es char ermöglichen, einen Alias ​​zu erstellen, und daher keine zulässige Optimierung für einen Compiler darstellt, obwohl dies in den meisten Fällen offensichtlich für einen Menschen korrekt ist Programmierer.

Sie haben auch darauf hingewiesen, dass das Herausheben des Vorgangs aus der Schleife in der Regel, jedoch nicht immer, eine Verbesserung in Bezug auf die Leistung darstellt und in Bezug auf die Lesbarkeit häufig negativ ist.

Ich möchte darauf hinweisen, dass es oft einen "dritten Weg" gibt. Anstatt bis zur gewünschten Anzahl von Iterationen zu zählen, können Sie bis Null herunterzählen. Dies bedeutet, dass die Anzahl der Iterationen nur einmal am Anfang der Schleife benötigt wird und danach nicht mehr gespeichert werden muss. Besser noch auf Assemblerebene erübrigt es oft die Notwendigkeit eines expliziten Vergleichs, da die Dekrementierungsoperation normalerweise Flags setzt, die angeben, ob der Zähler sowohl vor (Übertragsflag) als auch nach (Nullflag) der Dekrementierung Null war.

for (unsigned x = static_cast<unsigned>(bitmap->width);x > 0;  x--)
{
    *p++ = 0xAA;
    *p++ = 0xBB;
    *p++ = 0xCC;
}

Beachten Sie, dass diese Version der Schleife x-Werte im Bereich 1..Breite und nicht im Bereich 0 .. (width-1) liefert. Das spielt in Ihrem Fall keine Rolle, weil Sie x eigentlich für nichts verwenden, aber es ist etwas, dessen Sie sich bewusst sein müssen. Wenn Sie eine Countdown-Schleife mit x-Werten im Bereich 0 .. (width-1) wünschen, können Sie dies tun.

for (unsigned x = static_cast<unsigned>(bitmap->width); x-- > 0;)
{
    *p++ = 0xAA;
    *p++ = 0xBB;
    *p++ = 0xCC;
}

Sie können die Darstellungen in den obigen Beispielen auch loswerden, ohne sich Gedanken über die Auswirkungen auf Vergleichsregeln machen zu müssen, da Sie sie mit bitmap-> width nur direkt einer Variablen zuweisen.

24
plugwash

Ok, Leute, also habe ich mit GCC -O3 Gemessen (mit GCC 4.9 unter Linux x64).

Es stellt sich heraus, dass die zweite Version 54% schneller läuft!

Also, ich denke, Aliasing ist die Sache, über die ich nicht nachgedacht hatte.

[Bearbeiten]

Ich habe die erste Version mit allen mit __restrict__ Definierten Zeigern erneut versucht, und die Ergebnisse sind die gleichen. Seltsam. Entweder ist Aliasing nicht das Problem, oder der Compiler optimiert es aus irgendeinem Grund auch mit __restrict__ Nicht gut.

[Bearbeiten 2]

Ok, ich glaube, ich konnte beweisen, dass Aliasing das Problem ist. Ich habe meinen ursprünglichen Test wiederholt, diesmal mit einem Array anstelle eines Zeigers:

const std::size_t n = 0x80000000ull;
bitmap->width = n;
static unsigned char d[n*3];
std::size_t i=0;
for (unsigned x = 0;  x < static_cast<unsigned>(bitmap->width);  ++x)
{
    d[i++] = 0xAA;
    d[i++] = 0xBB;
    d[i++] = 0xCC;
}

Und gemessen (musste "-mcmodel = large" verwenden, um es zu verknüpfen). Dann habe ich versucht:

const std::size_t n = 0x80000000ull;
bitmap->width = n;
static unsigned char d[n*3];
std::size_t i=0;
unsigned width(static_cast<unsigned>(bitmap->width));
for (unsigned x = 0;  x < width;  ++x)
{
    d[i++] = 0xAA;
    d[i++] = 0xBB;
    d[i++] = 0xCC;
}

Die Messergebnisse waren die gleichen - es scheint, als ob der Compiler sie selbst optimieren konnte.

Dann habe ich die Originalcodes ausprobiert (mit einem Zeiger p), diesmal wenn p vom Typ std::uint16_t* Ist. Auch hier waren die Ergebnisse dieselben - aufgrund des strengen Aliasing. Dann habe ich versucht, mit "-fno-strict-aliasing" zu bauen, und wieder einen Zeitunterschied festgestellt.

23
Yaron Cohen-Tal
11
emlai

Die Frage, die ursprünglich gestellt wurde:

Lohnt es sich, es zu optimieren?

Und meine Antwort darauf (eine gute Mischung aus Up- und Down-Stimmen)

Lassen Sie den Compiler sich darum kümmern.

Der Compiler wird mit ziemlicher Sicherheit einen besseren Job machen als Sie. Und es gibt keine Garantie, dass Ihre "Optimierung" besser ist als der "offensichtliche" Code - haben Sie es gemessen?

Haben Sie, was noch wichtiger ist, Beweise dafür, dass der Code, den Sie optimieren, Auswirkungen auf die Leistung Ihres Programms hat?

Trotz der Ablehnungen (und jetzt sehen wir das Aliasing-Problem) bin ich immer noch glücklich damit als gültige Antwort. Wenn Sie nicht wissen, ob es sich lohnt, etwas zu optimieren, ist dies wahrscheinlich nicht der Fall.

Eine etwas andere Frage wäre natürlich:

Wie kann ich feststellen, ob es sich lohnt, ein Codefragment zu optimieren?

Muss Ihre Anwendung oder Bibliothek schneller ausgeführt werden als derzeit? Wartet der Benutzer zu lange? Prognostiziert Ihre Software das Wetter von gestern anstatt von morgen?

Nur Sie können das wirklich sagen, basierend darauf, wofür Ihre Software gedacht ist und was Ihre Benutzer erwarten.

Vorausgesetzt, Ihre Software muss optimiert werden, müssen Sie als Nächstes mit der Messung beginnen. Die Profiler teilen Ihnen mit, wo sich Ihr Code gerade befindet. Wenn Ihr Fragment nicht als Engpass angezeigt wird, lassen Sie es am besten in Ruhe. Profiler und andere Messwerkzeuge zeigen Ihnen auch, ob Ihre Änderungen einen Unterschied gemacht haben. Es ist möglich, Stunden damit zu verbringen, Code zu optimieren, nur um festzustellen, dass Sie keinen erkennbaren Unterschied gemacht haben.

Was meinst du überhaupt mit "optimieren"?

Wenn Sie keinen 'optimierten' Code schreiben, sollte Ihr Code so klar, klar und präzise wie möglich sein. Das Argument "Vorzeitige Optimierung ist böse" ist keine Entschuldigung für schlampigen oder ineffizienten Code.

Der optimierte Code opfert normalerweise einige der oben genannten Attribute für die Leistung. Dies kann das Einfügen zusätzlicher lokaler Variablen, Objekte mit einem größeren als dem erwarteten Bereich oder sogar das Umkehren der normalen Schleifenreihenfolge umfassen. All dies ist möglicherweise weniger klar oder prägnant. Dokumentieren Sie daher den Code (kurz!), Warum Sie dies tun.

Bei "langsamem" Code sind diese Mikrooptimierungen jedoch häufig der letzte Ausweg. Als erstes sollten Algorithmen und Datenstrukturen betrachtet werden. Gibt es eine Möglichkeit, die Arbeit überhaupt zu vermeiden? Können lineare Suchen durch binäre ersetzt werden? Wäre eine verknüpfte Liste hier schneller als ein Vektor? Oder eine Hash-Tabelle? Kann ich Ergebnisse zwischenspeichern? Wenn Sie hier gute „effiziente“ Entscheidungen treffen, kann dies häufig die Leistung um eine Größenordnung oder mehr beeinträchtigen!

10
Roddy

In dieser Situation verwende ich das folgende Muster. Es ist fast so kurz wie der erste Fall von Ihnen und ist besser als der zweite Fall, weil die temporäre Variable lokal in der Schleife bleibt.

for (unsigned int x = 0, n = static_cast<unsigned>(bitmap->width); x < n; ++x)
{
  *p++ = 0xAA;
  *p++ = 0xBB;
  *p++ = 0xCC;
}

Dies ist mit weniger als einem intelligenten Compiler, einem Debugbuild oder bestimmten Kompilierungsflags schneller.

Edit1: Das Platzieren einer konstanten Operation außerhalb einer Schleife ist ein gutes Programmiermuster. Es zeigt das Verständnis der Grundlagen der Maschinenbedienung, insbesondere in C/C++. Ich würde argumentieren, dass die Anstrengung, sich zu beweisen, bei Menschen liegen sollte, die dieser Praxis nicht folgen. Wenn der Compiler ein gutes Muster bestraft, ist dies ein Fehler im Compiler.

Edit2:: Ich habe meinen Vorschlag gegen den Originalcode auf vs2013 gemessen und% 1 verbessert. Können wir es besser machen? Eine einfache manuelle Optimierung verbessert die ursprüngliche Schleife auf einem x64-Computer um das Dreifache, ohne auf exotische Anweisungen zurückzugreifen. Der folgende Code setzt ein Little-Endian-System und eine korrekt ausgerichtete Bitmap voraus. TEST 0 ist original (9 Sek.), TEST 1 ist schneller (3 Sek.). Ich wette, jemand könnte das noch schneller machen, und das Ergebnis des Tests würde von der Größe der Bitmap abhängen. In absehbarer Zeit wird der Compiler in der Lage sein, den beständig schnellsten Code zu erzeugen. Ich befürchte, dass dies die Zukunft sein wird, in der der Compiler auch eine Programmierer-KI sein wird, sodass wir arbeitslos wären. Aber schreiben Sie jetzt einfach Code, der zeigt, dass Sie wissen, dass keine zusätzliche Operation in der Schleife erforderlich ist.

#include <memory>
#include <time.h>

struct Bitmap_line
{
  int blah;
  unsigned int width;
  Bitmap_line(unsigned int w)
  {
    blah = 0;
    width = w;
  }
};

#define TEST 0 //define 1 for faster test

int main(int argc, char* argv[])
{
  unsigned int size = (4 * 1024 * 1024) / 3 * 3; //makes it divisible by 3
  unsigned char* pointer = (unsigned char*)malloc(size);
  memset(pointer, 0, size);
  std::unique_ptr<Bitmap_line> bitmap(new Bitmap_line(size / 3));
  clock_t told = clock();
#if TEST == 0
  for (int iter = 0; iter < 10000; iter++)
  {
    unsigned char* p = pointer;
    for (unsigned x = 0; x < static_cast<unsigned>(bitmap->width); ++x)
    //for (unsigned x = 0, n = static_cast<unsigned>(bitmap->width); x < n; ++x)
    {
      *p++ = 0xAA;
      *p++ = 0xBB;
      *p++ = 0xCC;
    }
  }
#else
  for (int iter = 0; iter < 10000; iter++)
  {
    unsigned char* p = pointer;
    unsigned x = 0;
    for (const unsigned n = static_cast<unsigned>(bitmap->width) - 4; x < n; x += 4)
    {
      *(int64_t*)p = 0xBBAACCBBAACCBBAALL;
      p += 8;
      *(int32_t*)p = 0xCCBBAACC;
      p += 4;
    }

    for (const unsigned n = static_cast<unsigned>(bitmap->width); x < n; ++x)
    {
      *p++ = 0xAA;
      *p++ = 0xBB;
      *p++ = 0xCC;
    }
  }
#endif
  double ms = 1000.0 * double(clock() - told) / CLOCKS_PER_SEC;
  printf("time %0.3f\n", ms);

  {
    //verify
    unsigned char* p = pointer;
    for (unsigned x = 0, n = static_cast<unsigned>(bitmap->width); x < n; ++x)
    {
      if ((*p++ != 0xAA) || (*p++ != 0xBB) || (*p++ != 0xCC))
      {
        printf("EEEEEEEEEEEEERRRRORRRR!!!\n");
        abort();
      }
    }
  }

  return 0;
}
6
0kcats

Es gibt zwei Dinge zu beachten.

A) Wie oft wird die Optimierung ausgeführt?

Wenn die Antwort nicht sehr häufig ist, z. B. nur, wenn ein Benutzer auf eine Schaltfläche klickt, stört es Sie nicht, wenn Ihr Code dadurch unleserlich wird. Wenn die Antwort 1000-mal pro Sekunde ist, möchten Sie wahrscheinlich mit der Optimierung fortfahren. Wenn es auch nur ein bisschen komplex ist, schreiben Sie unbedingt einen Kommentar, um zu erklären, was los ist, um dem nächsten Mann zu helfen, der mitkommt.

B) Ist der Code dadurch schwieriger zu warten bzw. Fehler zu beheben?

Wenn Sie keinen großen Leistungszuwachs feststellen, ist es keine gute Idee, Ihren Code einfach kryptisch zu machen, um ein paar Uhrenticks zu sparen. Viele Leute werden Ihnen sagen, dass jeder gute Programmierer in der Lage sein sollte, sich den Code anzusehen und herauszufinden, was los ist. Das ist wahr. Das Problem ist, dass in der Geschäftswelt die zusätzliche Zeit, um das herauszufinden, Geld kostet. Also, wenn Sie es schöner machen können, dann tun Sie es. Ihre Freunde werden es Ihnen danken.

Das heißt, ich würde persönlich das B-Beispiel verwenden.

5
soulsabr

Der Vergleich ist falsch , da die beiden Codeausschnitte

for (unsigned x = 0;  x < static_cast<unsigned>(bitmap->width);  ++x)

und

unsigned width(static_cast<unsigned>(bitmap->width));
for (unsigned x = 0;  x<width ;  ++x)

sind nicht gleichwertig

Im ersten Fall ist width abhängig und nicht const, und man kann nicht davon ausgehen, dass es sich zwischen nachfolgenden Iterationen nicht ändern kann. Es kann also nicht optimiert werden, sondern muss bei jeder Schleife überprüft werden.

In Ihrem optimierten Fall wird einer lokalen Variablen irgendwann während der Programmausführung der Wert bitmap->width Zugewiesen. Der Compiler kann überprüfen, ob sich dies tatsächlich ändert.

Haben Sie über Multi-Threading nachgedacht, oder ist der Wert möglicherweise extern abhängig, sodass er flüchtig ist? Wie würde man erwarten, dass der Compiler all diese Dinge herausfindet, wenn man es nicht sagt?

Der Compiler kann nur so gut, wie es Ihr Code zulässt.

4
g24l

Lassen Sie den Compiler in der Regel die Optimierung für Sie durchführen, bis Sie festgelegt haben, dass Sie übernehmen sollen. Die Logik dafür hat nichts mit Leistung zu tun, sondern mit menschlicher Lesbarkeit. In der großen Mehrheit der Fälle ist die Lesbarkeit Ihres Programms wichtiger als seine Leistung. Sie sollten darauf abzielen, Code zu schreiben, der für einen Menschen leichter zu lesen ist, und sich dann nur dann um die Optimierung kümmern, wenn Sie überzeugt sind, dass die Leistung wichtiger ist als die Wartbarkeit Ihres Codes.

Sobald Sie feststellen, dass die Leistung von Bedeutung ist, sollten Sie einen Profiler für den Code ausführen, um festzustellen, welche Schleifen ineffizient sind, und diese individuell optimieren. In der Tat kann es Fälle geben, in denen Sie diese Optimierung durchführen möchten (insbesondere wenn Sie auf C++ migrieren, wo STL-Container beteiligt sind), aber die Kosten für die Lesbarkeit sind hoch.

Außerdem kann ich mir pathologische Situationen vorstellen, in denen der Code tatsächlich verlangsamt werden könnte. Angenommen, der Compiler konnte nicht beweisen, dass bitmap->width Während des Prozesses konstant war. Indem Sie die Variable width hinzufügen, erzwingen Sie, dass der Compiler eine lokale Variable in diesem Bereich verwaltet. Wenn diese zusätzliche Variable aus plattformspezifischen Gründen eine Optimierung des Stapelspeichers verhinderte, muss sie möglicherweise die Art und Weise, in der Bytecodes ausgegeben werden, neu organisieren und etwas weniger Effizientes produzieren.

Beispielsweise muss unter Windows x64 ein spezieller API-Aufruf __chkstk In der Präambel der Funktion aufgerufen werden, wenn die Funktion mehr als eine Seite lokaler Variablen verwendet. Diese Funktion gibt Windows die Möglichkeit, die Schutzseiten zu verwalten, mit denen der Stapel bei Bedarf erweitert wird. Wenn Ihre zusätzliche Variable die Stapelverwendung von unter 1 Seite auf mindestens 1 Seite erhöht, muss Ihre Funktion jetzt bei jeder Eingabe __chkstk Aufrufen. Wenn Sie diese Schleife auf einem langsamen Pfad optimieren, können Sie den schnellen Pfad tatsächlich mehr verlangsamen, als Sie auf dem langsamen Pfad gespeichert haben!

Sicher, es ist ein bisschen pathologisch, aber der Punkt dieses Beispiels ist, dass Sie den Compiler tatsächlich verlangsamen können. Es zeigt nur, dass Sie Ihre Arbeit profilieren müssen, um festzustellen, wohin die Optimierungen führen. In der Zwischenzeit sollten Sie die Lesbarkeit in keiner Weise für eine Optimierung opfern, die von Bedeutung sein kann oder nicht.

4
Cort Ammon

Der Compiler kann viele Dinge optimieren. Für Ihr Beispiel sollten Sie sich für die Lesbarkeit, Wartbarkeit und die Einhaltung Ihres Codestandards entscheiden. Weitere Informationen dazu, was (mit GCC) optimiert werden kann, finden Sie unter dieser Blog-Beitrag .

4

Wenn Sie nicht genau wissen, wie der Compiler den Code optimiert, sollten Sie Ihre eigenen Optimierungen vornehmen, indem Sie die Lesbarkeit und das Design des Codes beibehalten. Praktisch ist es schwierig, den Assembly-Code für jede Funktion zu überprüfen, die wir für neue Compiler-Versionen schreiben.

2
Vinayak S M

Der Compiler kann bitmap->width Nicht optimieren, da der Wert von width zwischen den Iterationen geändert werden kann. Es gibt einige der häufigsten Gründe:

  1. Multithreading. Der Compiler kann nicht vorhersagen, ob ein anderer Thread den Wert ändern wird.
  2. Änderung innerhalb der Schleife, manchmal ist es nicht einfach zu sagen, ob die Variable innerhalb der Schleife geändert wird.
  3. Es ist ein Funktionsaufruf, z.B. iterator::end() oder container::size(), daher ist es schwer vorherzusagen, ob immer das gleiche Ergebnis zurückgegeben wird.

Um zusammenzufassen (meine persönliche Meinung), für Orte, die ein hohes Maß an Optimierung erfordern, müssen Sie dies selbst tun. An anderen Orten lassen Sie es einfach, der Compiler kann es optimieren oder nicht, wenn es keinen großen Unterschied gibt. Die Lesbarkeit des Codes ist das Hauptziel.

1
ST3