Frage

Ich frage mich immer, warum Compiler kann nicht einfach Dinge herauszufinden, die für das menschliche Auge offensichtlich sind. Sie haben viele einfache Optimierungen, aber nie etwas sogar ein wenig komplexer. Zum Beispiel nimmt dieser Code ca. 6 Sekunden auf meinem Computer den Wert Null (unter Verwendung von Java 1.6) zu drucken:

int x = 0;
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
    x += x + x + x + x + x;
}
System.out.println(x);

Es ist völlig klar, dass x nie Sache so nicht geändert wird, wie oft Sie 0 bis sich hinzuzufügen bleibt Null. So könnte der Compiler in der Theorie ersetzt diese mit System.out.println (0).

oder noch besser, das dauert 23 Sekunden:

public int slow() {
    String s = "x";
    for (int i = 0; i < 100000; ++i) {
        s += "x";
    }
    return 10;
}

Zuerst könnte der Compiler feststellen, dass ich tatsächlich einen String s von 100000 „x“ erschaffe so automatisch s String statt, oder noch besser direkt ersetzen Sie es mit dem resultierenden String, wie es ist immer das gleiche verwenden könnte. Zweitens ist es nicht erkennt, dass ich eigentlich gar nicht die Zeichenfolge verwenden, so dass die ganze Schleife verworfen werden könnte!

Warum, nach so viel Arbeitskräfte in schnelle Compiler gehen wird, sind sie noch so relativ stumm?

Bearbeiten : Natürlich sind die dummen Beispiele, die nie überall dort eingesetzt werden sollten. Aber immer, wenn ich unlesbar einen schönen und sehr lesbaren Code in etwas neu zu schreiben, so dass der Compiler glücklich ist und produziert schnellen Code, frage ich mich, warum Compiler oder ein anderes automatisiertes Tool kann diese Arbeit für mich nicht.

War es hilfreich?

Lösung

Oh, ich weiß es nicht. Manchmal sind Compiler ziemlich smart. Betrachten Sie das folgende C-Programm:

#include <stdio.h>  /* printf() */

int factorial(int n) {
   return n == 0 ? 1 : n * factorial(n - 1);
}

int main() {
   int n = 10;

   printf("factorial(%d) = %d\n", n, factorial(n));

   return 0;
}

Auf meiner Version von GCC (4.3.2 auf Posting auf meinem Blog ).

Andere Tipps

Meiner Meinung nach, ich glaube nicht, es ist die Aufgabe des Compilers zu beheben, was ist, ehrlich gesagt, schlechte Codierung. Sie haben, ganz explizit, sagte der Compiler Sie möchten, dass erste Schleife ausgeführt. Es ist das gleiche wie:

x = 0
sleep 6 // Let's assume this is defined somewhere.
print x

Ich würde die Compiler zu entfernen meine sleep Anweisung nicht will, nur weil es nichts tat. Sie können argumentieren, dass die Schlaf-Anweisung ist eine explizite Anforderung für eine Verzögerung, während Ihr Beispiel nicht der Fall ist. Aber dann werden Sie die Compiler werden, so dass machen, sehr hohe Ebene Entscheidungen über das, was Ihr Code tun soll, und ich glaube, dass eine schlechte Sache zu sein.

-Code, und der Compiler, die es verarbeitet, sind Werkzeuge, und Sie benötigen ein Werkzeug-Schmied sein, wenn Sie wollen, sie effektiv nutzen. Wie viele 12" Kettensägen wird zu versuchen, sich weigern, eine 30" abgeholzt Baum? Wie viele Bohrer schaltet automatisch Modus hämmern, wenn sie eine Betonwand erkennen?

Keine, ich vermute, und das ist, weil die Kosten für diese in das Produkt der Gestaltung für einen Start horrend sein würden. Aber noch wichtiger ist, sollten Sie nicht mit Bohrern oder Kettensägen, wenn Sie nicht wissen, was du tust. Zum Beispiel: Wenn Sie nicht wissen, was ein Rückschlag ist (eine sehr einfache Art und Weise für ein Neuling den Arm zu nehmen off), bleiben weg von Kettensägen, bis Sie tun

.

Ich bin für so dass Compiler vorschlägt Verbesserungen, aber ich würde lieber die Kontrolle selbst halten. Es sollte nicht bis zu den Compiler sein, einseitig zu entscheiden, dass eine Schleife nicht erforderlich ist.

Zum Beispiel, ich habe Timing getan Schleifen in Embedded-Systemen, bei denen die Taktgeschwindigkeit der CPU genau bekannt ist, aber kein zuverlässiges Zeitmessgerät zur Verfügung steht. In diesem Fall können Sie genau berechnen, wie lange eine bestimmte Schleife nehmen und steuern, wie oft Dinge passieren. Das würde nicht funktionieren, wenn der Compiler (oder Assembler in diesem Fall) beschlossen, meine Schleife nutzlos war und optimierte es aus der Existenz.

Having said that, lassen Sie mich verlassen Sie mit einer alten Geschichte eines VAX Fortran-Compiler, der einen Maßstab für die Leistung unterzogen wurde und es wurde festgestellt, dass es viele um Größenordnungen schneller als der nächste Wettbewerber .

Es stellt sich heraus, der Compiler bemerkt, dass das Ergebnis der Benchmark-Schleifen wurden nirgendwo anders verwendet und optimiert, um die Schlingen in Vergessenheit.

Beim Reden von einer C / C ++ Sicht:

Ihr erstes Beispiel wird von den meisten Compiler optimiert werden. Wenn der Java-Compiler von Sun wirklich diese Schleife führt es ist der Fehler Compiler, aber mein Wort, dass alle nach 1990 C, C ++ oder Fortran-Compiler vollständig eine solche Schleife eliminiert.

Ihr zweites Beispiel kann nicht in den meisten Sprachen optimiert werden, da die Speicherzuordnung als zusammen die Saiten der verketten Nebenwirkung geschieht. Wenn ein Compiler den Code das Muster der Speicherzuweisung optimieren würde würde sich ändern, und dies kann zu Effekten führen könnte, dass der Programmierer zu vermeiden versucht. Speicherfragmentierung und die damit verbundenen Probleme sind Probleme, die Embedded-Programmierer immer noch jeden Tag stellen.

Insgesamt bin ich zufrieden mit den Optimierungen Compiler kann in diesen Tagen tun.

Compiler sind so konzipiert, dass berechenbar . Dies kann sie von Zeit zu Zeit dumm aussehen lassen, aber das ist OK. Der Compiler Schriftsteller Ziele sind

  • Sie sollten Ihren Code aussehen können, und vernünftige Prognosen über seine Leistung machen.

  • Kleine Änderungen im Code sollte in der drastischen Unterschiede in der Leistung zur Folge haben.

  • Wenn eine kleine Änderung an den Programmierer sieht aus wie es die Leistung verbessern sollte, sollte es zumindest nicht die Leistung beeinträchtigen (es sei denn, überraschende Dinge in der Hardware geschehen).

Alle sprechen diese Kriterien gegen die „Magie“ Optimierungen, die nur für Sonderfälle gelten.


Ihre beiden Beispiele haben ein Variable in einer Schleife aktualisiert, aber nicht anderswo verwendet. Dieser Fall ist tatsächlich ziemlich schwierig zu holen, wenn Sie irgendeine Art von Rahmen verwenden, die Dead-Code-Elimination mit anderen Optimierungen wie eine Kopierausbreitung oder Konstantenpropagation kombinieren. Zu einem einfach Datenfluß Optimierer ist die Variable nicht tot sehen. Um zu sehen, zu verstehen, warum dieses Problem schwierig ist, das Papier von Lernern, Grove, und Chambers in POPL 2002 , die eben dieses Beispiel verwendet und erklärt, warum es schwer ist.

Der Compiler HotSpot JIT wird nur Code optimieren, die seit einiger Zeit in Betrieb war. Durch die Zeit, Ihr Code heiß ist, hat die Schleife bereits gestartet wurde und die JIT-Compiler bis zum nächsten Mal warten müssen, das Verfahren eingegeben wird nach Wegen zu suchen, um die Schleife zu optimieren entfernt. Wenn Sie die Methode mehrmals aufrufen, können Sie eine bessere Leistung sehen.

Dies ist in der HotSpot FAQ unter der Frage „ich schreibe eine einfache Bedienung zu Zeit eine einfache Schleife und es ist langsam. Was mache ich falsch?“.

Im Ernst? Warum sollte jemand jemals reale Welt Code wie das schreiben? IMHO, der Code, nicht der Compiler ist der „dumme“ entity hier. Ich für meinen Teil ist vollkommen glücklich, dass Compiler Schriftsteller nicht die Mühe, ihre Zeit zu verschwenden versucht, so etwas zu optimieren.

Bearbeiten / Klarstellung: Ich weiß, dass der Code in der Frage als Beispiel gemeint ist, aber das beweist nur, meinen Punkt: Sie können entweder versuchen, sein, oder ziemlich ahnungslos seine höchst ineffizienten Code so zu schreiben. Es ist nicht die Aufgabe des Compilers unsere Hand zu halten, so dass wir nicht schrecklich Code schreiben. Es ist unsere Verantwortung, wie die Menschen, die den Code schreiben wissen genug über unsere Werkzeuge effizient zu schreiben und deutlich.

Nun, ich kann sprechen nur von C ++, weil ich total einen Java-Anfänger bin. In C ++ Compiler sind frei, alle sprachlichen Anforderungen der Norm gestellt außer Acht zu lassen, solange die beobachtbar Verhalten as-if der Compiler alle Regeln tatsächlich emuliert, die platziert werden von der Norm. Beobachtbares Verhalten ist definiert als jeder liest und schreibt auf flüchtige Daten an und ruft zu Bibliotheksfunktionen . Bedenken Sie:

extern int x; // defined elsewhere
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
    x += x + x + x + x + x;
}
return x;

Die C ++ Compiler erlaubt ist, dieses Stück Code zu optimieren und nur den richtigen Wert zu x hinzufügen, die einmal aus dieser Schleife führen würde, da der Code verhält sich wie-wenn die Schleife nie passiert, und keine flüchtigen Daten, noch Bibliotheksfunktionen beteiligt sind, die Nebenwirkungen erforderlich verursachen könnte. Betrachten wir nun flüchtige Variablen:

extern volatile int x; // defined elsewhere
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
    x += x + x + x + x + x;
}
return x;

Der Compiler ist nicht erlaubt die gleiche Optimierung mehr zu tun, weil es nicht, dass Nebenwirkungen, indem er an x verursacht nicht nachweisen kann, das beobachtbare Verhalten des Programms beeinflussen könnten. Schließlich könnte x in einer Speicherzelle durch ein Hardware-Gerät angeschaut werden eingestellt, die bei jedem Schreib auslösen würden.


Apropos Java, ich habe Ihre Schleife getestet, und es kommt vor, dass die GNU Java Compiler (gcj) in unmäßig viel Zeit, um Ihre Schleife zu beenden dauert (es einfach nicht fertig, und ich tötete es). Ich ermöglichte Optimierungsflags (-O2) und es kam es ausgedruckt 0 sofort:

[js@HOST2 java]$ gcj --main=Optimize -O2 Optimize.java
[js@HOST2 java]$ ./a.out
0
[js@HOST2 java]$

Vielleicht könnte diese Beobachtung in diesem Thread hilfreich sein? Warum passiert es für gcj so schnell sein? Nun, ein Grund ist sicherlich, dass gcj in Maschinencode kompiliert, und so hat es keine Möglichkeit, dass Code zu optimieren, basierend auf Laufzeitverhalten des Codes. Es nimmt alle seine strong zusammen und versucht, so viel wie es bei der Kompilierung kann zu optimieren. Eine virtuelle Maschine kann jedoch kompilieren Code Just in Time, da diese Ausgabe von Java für diesen Code zeigt:

class Optimize {
    private static int doIt() {
        int x = 0;
        for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
            x += x + x + x + x + x;
        }
        return x;
    }
    public static void main(String[] args) {
        for(int i=0;i<5;i++) {
            doIt();
        }
    }
}

Ausgabe für java -XX:+PrintCompilation Optimize:

1       java.lang.String::hashCode (60 bytes)
1%      Optimize::doIt @ 4 (30 bytes)
2       Optimize::doIt (30 bytes)

Wie wir sehen, es JIT kompiliert die doIt Funktion 2 mal. Basierend auf der Beobachtung der ersten Ausführung, kompiliert sie es ein zweites Mal. Aber es kommt die gleiche Größe wie Bytecode zweimal haben, was darauf hindeutet, die Schleife ist noch an seinem Platz.

Als ein anderer Programmierer zeigt, die Ausführungszeit für bestimmte tote Schleifen wird auch für einige Fälle erhöht, um anschließend kompilierten Code. Er berichtete, einen Fehler, der hier gelesen werden kann, und ist ab 24 . Oktober 2008.

Auf dem ersten Beispiel, es ist eine Optimierung, die nur funktioniert, wenn der Wert Null ist. Die zusätzliche if Anweisung im Compiler für diesen einen suchen brauchte selten gesehen Klausel kann nicht nur wert sein (da es wird dafür auf jedem einzelnen Variable überprüfen). Darüber hinaus, was dazu:

int x = 1;
int y = 1;
int z = x - y;
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
    z += z + z + z + z + z;
}
System.out.println(z);

Dies ist offensichtlich immer noch die gleiche, aber jetzt gibt es einen extra Fall, dass wir in den Compiler Code. Es gibt nur eine unendliche Menge von Möglichkeiten, dass es Null am Ende wird können, die es nicht wert sind Codierung für, und ich denke, man könnte sagen, dass wenn Sie einen von ihnen haben werden würden Sie könnte genauso gut haben sie alle.

Einige Optimierungen Sie kümmern sich um das zweite Beispiel, das Sie gebucht haben, aber ich glaube, ich habe es in funktionalen Sprachen mehr zu sehen und nicht so viel Java. Die große Sache, die es schwer, in neueren Sprachen macht, ist Affe-Patchen . Jetzt können += haben ein Nebeneffekt das heißt, wenn wir sie optimieren heraus, es ist möglicherweise falsch (zB Funktionalität += hinzufügen, das der aktuelle Wert druckt wird insgesamt ein anderes Programm bedeuten).

Aber es kommt auf die gleiche Sache noch einmal. Es gibt einfach zu viele Fälle haben Sie würde suchen keine Nebenwirkungen durchgeführt, um sicherzustellen, werden die möglicherweise wird das endgültige Programm des Staates ändern

Es ist nur einfacher, einen zusätzlichen Moment zu nehmen, und stellen Sie sicher, was Sie schreiben, ist, was Sie wirklich der Computer tun wollen. :)

Compiler im Allgemeinen ist sehr intelligent.

Was müssen Sie beachten ist, dass sie für jede möglicherweise Ausnahme oder eine Situation berücksichtigen müssen, wo die Optimierung oder Refactoring Code unerwünschte Nebenwirkungen verursachen können.

Dinge wie, Gewinde Programme, Zeiger Aliasing, dynamisch verknüpften Code und Nebenwirkungen (Systemaufrufe / Speicher alloc) usw. formal machen prooving Refactoring sehr schwierig.

Auch wenn Ihr Beispiel einfach ist, kann es nach wie vor schwierige Situationen zu prüfen sein.

Wie für Ihr String Argument, das ist kein Compiler Job, die Datenstrukturen zur Auswahl für Sie zu nutzen.

Wenn Sie leistungsfähigeren Optimierungen bewegen, um eine stark typisierte Sprache wie Fortran oder Haskell, wo die Compiler viel mehr Informationen gegeben werden, mit zu arbeiten.

Die meisten Kurse Lehre Compiler / Optimierung (auch acedemically) geben ein Gefühl der Wertschätzung, wie gerneral formal prooven optimisatons machen anstatt spezifische Fälle Hacking ist ein sehr schwieriges Problem.

Ich glaube, Sie unterschätzen, wie viel Arbeit es sicher zu machen ist, dass ein Stück Code nicht ein weiteres Stück Code nicht beeinträchtigt. Mit nur einer kleinen Änderung Ihrer Beispiele x, i und s konnte alle auf den gleichen Speicher. Sobald eine der Variablen ein Zeiger ist, ist es viel schwieriger zu erklären, welche Code könnte je Nebenwirkungen haben auf ist Punkt zu dem, was.

Außerdem glaube ich, Leute, das Programm compliers eher Zeit machen Optimierungen verbringen würde, die nicht so einfach sind für die Menschen zu tun.

Weil wir einfach noch nicht da sind. Sie könnten genauso gut gefragt, „warum muss ich immer noch Programme schreiben ... warum kann ich nicht füttern nur im Anforderungsdokument und den Computer für mich, die Anwendung schreiben?“

Compiler Schriftsteller verbringt Zeit auf die kleinen Dinge, denn das ist die Art von Dingen ist, dass Anwendungsprogrammierer verpassen neigen.

Auch sie kann nicht zu viel davon ausgehen, (vielleicht war die Schleife eine Art Ghetto Zeitverzögerung oder etwas)?

Es ist ein ewiges Wettrüsten zwischen Compiler Schriftstellern und Programmierern.

Nicht erfundene Beispiele lassen sich gut - die meisten Compiler optimiere in der Tat die offensichtlich nutzlos Code entfernt

.

Ausgeklügelte untersucht wird immer die Compiler Stumpf. Der Nachweis, wenn überhaupt nötig war, dass jeder Programmierer ist intelligenter als jedes Programm.

In Zukunft werden Sie brauchen mehr erfundene Beispiele als die jemandes Sie hier gepostet haben.

Wie andere haben den ersten Teil Ihrer Frage angemessen behandelt, ich werde versuchen, den zweiten Teil in Angriff zu nehmen, das heißt „automatisch verwendet Stringbuilder statt“.

Es gibt mehrere gute Gründe, nicht zu tun, was Sie vorgeschlagen, aber der größte Faktor in der Praxis ist wahrscheinlich, dass der Optimierer lange läuft, nachdem der eigentliche Quellcode etwa wurde verdaut und vergessen. Optimizern allgemeine entweder auf dem erzeugten Bytecode (oder Montag, drei Adresscode, Maschinencode, etc.), oder auf dem abstrakten Syntaxbaum, die aus der Code-Parsing führen zu betreiben. Optimizers im Allgemeinen nichts von den Laufzeitbibliotheken wissen (oder irgendwelche Bibliotheken überhaupt), und statt auf der Befehlsebene arbeiten (das heißt, geringer Steuerfluss und Registerzuweisung).

Zweitens, wie Bibliotheken entwickeln (insb. In Java) viel schneller als Sprachen, mit ihnen Schritt zu halten und zu wissen, was deprecates was und was andere Bibliothekskomponente besser geeignet sein könnte, um die Aufgabe, eine Herkules-Aufgabe sein würde. Wahrscheinlich auch ein unmöglicher, als diese vorgeschlagenen Optimierer müßten genau verstehen sowohl Ihre Absicht und die Absicht der verfügbaren Bibliothekskomponente, und irgendwie eine Zuordnung zwischen ihnen finden.

Schließlich, wie andere gesagt haben (glaube ich), kann der Compiler / Optimierer Schreiber vernünftigerweise davon ausgehen, dass der Programmierer den eingegebenen Code zu schreiben ist nicht hirntot. Es wäre eine Verschwendung von Zeit erhebliche Anstrengungen zu widmen besondere Fälle wie diese idiotisch, wenn andere, allgemeinere Optimierungen gibt es zuhauf. Außerdem haben wie andere auch erwähnt, scheinbar hirntoten Code kann einen tatsächlichen Zweck haben (ein Spin-Lock, busy wait vor einer Systemebene Ausbeute, etc.), und der Compiler hat zu ehren, was der Programmierer bittet um (wenn es ist syntaktisch und semantisch gültig).

Haben Sie kompilieren Code veröffentlichen? Ich denke, ein guter Compiler in Ihrem zweiten Beispiel feststellt, dass die Zeichenfolge nie verwendet wird, eine die gesamte Schleife entfernt.

Eigentlich Java sollte String-Builder in Ihrem zweiten Beispiel.

Das grundlegende Problem mit dem Versuch, diese Beispiele zu optimieren weg ist, dass es erfordern würde tun Theorembeweisen . Was bedeutet, dass der Compiler einen mathematischen Beweis konstruieren müßte, was Sie Code tatsächlich tun wird. Und das ist nicht bei allen kleine Aufgabe. In der Tat, zu beweisen, dass in der Lage, alle Code wirklich eine Wirkung hat, ist äquivalent zu dem Halteproblem.

Natürlich können Sie mit trivialen Beispiele kommen, aber die Zahl der trivialen Beispiele ist unbegrenzt. Man könnte immer etwas anderes denken, so gibt es keine Möglichkeit, sie alle zu fangen.

Natürlich ist es möglich, für einig Code nachgewiesen wird keine Auswirkungen zu haben, wie in Ihrem Beispiel. Was würden Sie tun möchten, ist der Compiler haben jedes Problem optimieren weg, die in P Zeit ungenutzt nachgewiesen werden kann.

Aber wie auch immer, das ist eine Menge Arbeit, und es wird nicht Sie alle so viel. Die Menschen verbringen aus mit Bugs in ihnen viel Zeit, um herauszufinden, Wege versuchen, Programme zu verhindern, und Systeme, wie sie in Java und Scala Typ Versuche sind Fehler zu verhindern, aber im Moment niemand Typ Systeme wird mit Aussagen über die Ausführungszeit machen , soweit ich weiß.

Sie möchten vielleicht in Haskel suchen, die ich denke, die fortgeschrittenste Theorie beweisen Zeug hat, obwohl ich auf, dass nicht sicher bin. Ich weiß es selbst nicht.

Meistens, was Sie beschweren sich über ist ‚warum sind Java-Compiler so dumm‘, da die meisten anderen Sprache Compiler sind viel klüger.

Der Grund für die Dummheit des Java-Compiler ist historisch. Zuerst wurden die ursprünglichen Java-Implementierungen Interpreter basiert und wurde Leistung consisdered unwichtig. Zweitens waren viele der ursprünglichen Java-Benchmarks problematisch zu optimieren. Ich erinnere mich an eine Benchmark, die viel wie Ihr zweites Beispiel aussah. Leider, wenn die Compiler die Schleife weg optimieren, würde die Benchmark eine Division durch Null Ausnahme erhalten, wenn es versucht, eine Baseline-Zahl durch die verstrichene Zeit zu teilen seine Leistung Punktzahl zu berechnen. Also, wenn eine Optimierung der Java-Compiler zu schreiben, mußte man sehr vorsichtig sein, nicht um einige Dinge zu optimieren, da die Menschen würden dann Compiler Anspruch gebrochen wurde.

Es ist fast schlechte Praxis betrachtet Dinge wie diese zu optimieren, wenn auf JVM-Bytecode kompiliert unten. Sun javac hat einige grundlegende Optimierungen haben, ebenso wie scalac, groovyc usw. Kurz gesagt, alles, was wirklich sprachspezifische ist innerhalb der Compiler optimiert bekommen. Doch Dinge wie diese, die offensichtlich sind so gekünstelt als Agnostiker sein Sprache durch einfach aus Politik rutscht.

Der Grund dafür ist es HotSpot eine viel einheitlichere Ansicht des Bytecode und seine Muster haben kann. Wenn die Compiler Ausmisten etwa mit Kanten Fällen beginnen, das reduziert die VM der Fähigkeit den allgemeinen Fall zu optimieren, die nicht bei der Kompilierung offensichtlich sein können. Steve Yeggie mag über diese Harfe auf: Optimierung ist oft leichter , wenn zur Laufzeit durch eine geschickte virtuelle Maschine ausgeführt. Er geht sogar so weit, zu behaupten, dass HotSpot javac die Optimierungen abstreift. Während ich weiß nicht, ob das wahr ist, es würde mich nicht überraschen.

Um es zusammenzufassen: Compiler VMs haben eine sehr unterschiedliche Gruppe von Targeting-Kriterien, insbesondere im Bereich der Optimierung und wenn es angemessen ist. Gehen Sie nicht die Compiler-Autoren die Schuld für das Verlassen der Arbeit an den weit mehr fähigen JVM. Wie mehrmals auf diesen Thread ausgeführt hat, sind moderne Compiler die native Architektur (wie die gcc Familie) Targeting extrem klug, obszön schnellen Code durch einige sehr intelligente Optimierungen zu erzeugen.

Ich habe nie den Punkt in Beseitigung von totem Code in erster Linie gesehen. Warum schreibt der Programmierer es ?? Wenn Sie vorhaben, etwas über toten Code zu tun, erklärt es einen Compiler-Fehler! Es bedeutet, mit ziemlicher Sicherheit der Programmierer einen Fehler gemacht - und für die wenige Fälle ist es nicht, ein Compiler-Direktive eine Variable zu verwenden wäre die richtige Antwort sein. Wenn ich tot Code in einer Routine setzen will ich es ausgeführt -. Ich bin wahrscheinlich die Ergebnisse in den Debugger zu inspizieren Planung

Der Fall, in dem der Compiler etwas Gutes tun könnte, ist Schleifeninvarianten ziehen. Manchmal sagt Klarheit die Berechnung in der Schleife zu codieren und die Pull-Compiler mit einem solchen Dingen wäre gut.

Compiler die strengen Aliasing Optimierungen tun kann, wird erstes Beispiel optimiert aus. Siehe hier .

Zweites Beispiel kann nicht optimiert werden, da der langsamste Teil hier Speicherzuweisung / Neuzuteilung ist und Operator + = in eine Funktion neu definiert, die die Speicher Sachen tun. Verschiedene Implementierungen von Strings verwenden unterschiedliche Allokationsstrategien.

Ich selbst würde auch eher wie malloc haben (100000) als tausend malloc (100) auch wenn dabei s + = "s"; aber jetzt ist das Ding außerhalb des Bereichs von Compilern und hat von Menschen optimiert werden. Dies ist, was D Sprache durch die Einführung zu lösen versucht reine Funktionen .

Wie in anderen Antworten hier erwähnt, Perl tut zweites Beispiel in weniger als eine Sekunde, weil es mehr Speicher reserviert als für alle Fälle mehr Speicher angefordert wird später benötigt werden.

Im Release-Modus VS 2010 C ++ nimmt dies nicht jederzeit ausgeführt werden. Allerdings Debug-Modus ist eine andere Geschichte.

#include <stdio.h>
int main()
{
    int x = 0;
    for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
        x += x + x + x + x + x;
    }
    printf("%d", x);
}

Absolute-Optimierung ist ein unentscheidbar Problem, das heißt, es gibt keine Turing-Maschine (und daher kein Computerprogramm), die die optimale Version von jedem Programm ergeben kann.

Einige einfache Optimierungen können (und in der Tat sind) gemacht, aber in den Beispielen Sie gab ...

  1. Um festzustellen, dass Ihr erstes Programm immer Null druckt, würde der Compiler erkennen hat, dass x konstant bleibt trotz aller Schleifendurchläufe. Wie können Sie erklärt (Ich weiß, es ist nicht das beste Wort ist, aber ich kann mit einem anderen nicht kommen), das zu einem Compiler?

  2. Wie kann der Compiler weiß, dass der String das richtige Werkzeug für den Job ohne Bezug auf das?

In einer realen Anwendung, wenn die Effizienz in einem Teil Ihrer Anwendung kritisch ist, muss sich in einer Low-Level-Sprache wie C geschrieben werden. (Haha, im Ernst, ich schreibe das? )

Dies ist ein Beispiel für prozeduralen Code v. Funktionscode.

Sie haben ein Verfahren für die Compiler detailliert zu folgen, so dass die Optimierungen werden rund um die Prozedur zu basieren detaillierten und Nebenwirkungen minimieren oder nicht optimieren, wo es nicht tun, was Sie erwarten. Dies macht es einfacher zu debuggen.

Wenn Sie in einer Funktionsbeschreibung setzen, was Sie wollen zum Beispiel. SQL, dann sind Sie den Compiler eine breite Palette von Optionen geben zu optimieren.

Vielleicht eine Art von Code-Analyse wäre in der Lage, diese Art von Problem oder Profilierung zur Laufzeit zu finden, aber dann werden Sie will, um die Quelle zu etwas vernünftiger ändern.

Da Compiler Schriftsteller Optimierungen für Dinge versuchen, hinzufügen, dass die Materie (hoffe ich) und die gemessen werden, in * Stein Benchmarks (ich fürchte).

Es gibt Myriaden von anderen möglichen Codefragmenten wie das Ihre, die nichts tun und mit zunehmender Anstrengung auf dem Compiler Schriftsteller optimiert werden könnte, die aber kaum je begegnet.

Was ich peinlich fühle, ist, dass auch heute die meisten Compiler Code generiert für die switchValue größer als 255 für eine dichte oder fast vollständige Umstellung auf einem nicht signierten Zeichen zu überprüfen. Das summiert sich 2 Anweisungen zu den meisten Bytecode-Interpreter der inneren Schleife.

Ich hasse es auf so einem alten Frage zu bringen (wie habe ich hier eigentlich?), Aber ich denke, ein Teil von etwas, von einem holdout aus den Tagen des Commodore 64 sein könnte.

In den frühen 1980er Jahren, lief alles auf einem festen Takt. Es gab keinen Turbo Boosting und Code wurde immer für ein bestimmtes System mit einem bestimmten Prozessor und speziellem Speicher erstellt usw. In Commodore BASIC, die Standardmethode für delays Umsetzung sah aus wie eine Menge:

10 FOR X = 1 TO 1000
20 NEXT : REM 1-SECOND DELAY

(Eigentlich in der Praxis ist es ähnelte enger 10FORX=1TO1000:NEXT, aber Sie wissen, was ich meine.)

Wenn sie dies zu optimieren, wäre es alles-nichts bricht immer zeitlich abgestimmt werden würde. Ich weiß nicht von irgendwelchen Beispiele, aber ich bin sicher, es gibt viele kleine Dinge wie diese durch die Geschichte der kompilierten Sprachen zerstreut werden, die verhindert, dass die Dinge optimiert.

Zugegeben, diese nicht-Optimierungen nicht heute notwendig. Wahrscheinlich gibt es jedoch einige unausgesprochene Regel unter Compiler-Entwickler nicht zu optimieren, Dinge wie diese. Ich möchte nicht wissen.

Nur froh sein, dass der Code etwas optimiert ist, im Gegensatz zu Code auf dem C64. Anzeigen einer Bitmap auf dem C64 könnte nehmen bis zu 60 Sekunden mit den effizientesten BASIC Schleifen; so, die meisten Spiele, etc. wurden in Maschinensprache geschrieben. Schreiben Spiele in Maschinensprache macht keinen Spaß.

Nur meine Gedanken.

Premise: Ich studierte Compiler an der Universität.

Der javac Compiler extrem dumm ist und führt absolut keine Optimierung, weil es auf dem Java-Runtime setzt sie zu tun. Die Laufzeit wird das Ding fangen und zu optimieren, aber es wird es nur fangen, nachdem die Funktion ein paar tausend Mal ausgeführt wird.

Wenn Sie einen besseren Compiler verwenden (wie gcc) Optimierungen ermöglichen, wird es Ihren Code optimieren, weil es ganz offensichtlich, dass die Optimierung zu tun ist.

Compiler sind so schlau wie wir sie machen. Ich weiß nicht, zu viele Programmierer, die einen Compiler schreiben stören würde, die für die Konstrukte wie die, die überprüfen möchten Sie verwendet. Die meisten konzentrieren sich auf mehr typischen Möglichkeiten, um die Leistung zu verbessern.

Es ist möglich, dass ein Tag werden wir Software, einschließlich Compiler, die tatsächlich lernen und wachsen können. Wenn an diesem Tag die meisten kommt, vielleicht alle, werden Programmierer aus Job sein.

Die Bedeutung Ihrer beiden Beispiele ist sinnlos, nutzlos und nur gemacht, den Compiler zu täuschen.

Der Compiler ist nicht in der Lage (und soll nicht) die Bedeutung eines Verfahren, eine Schleife oder ein Programm zu sehen. Das ist, wo Sie in das Bild zu bekommen. Sie legen ein Verfahren für eine bestimmte Funktionalität / Bedeutung, egal wie dumm es ist. Es ist der gleiche Fall für einfache Probleme oder extreme komplexe Programme.

In Ihrem Fall der Compiler könnte es optimieren, weil es „denkt“ es soll optimiert werden in einer anderen Art und Weise, aber warum dort bleiben?

Extreme andere Situation. Wir haben einen Smart-Compiler von Windows. Tonnen von Code zu kompilieren. Aber wenn es klug ist, es läuft es auf drei Zeilen Code unten ...

"starting windows"
"enjoy freecell/solitaire"
"shutting down windows"

Der Rest des Codes ist veraltet, weil es nie verwendet wird, berührt, abgerufen. Haben wir das wirklich wollen?

Es zwingt Sie (der Programmierer), um darüber nachzudenken, was Sie schreiben. Erzwingen Compiler Ihre Arbeit für Sie tun jemand nicht hilft: Es macht die Compiler viel komplexer, und es macht Sie dümmer und weniger aufmerksam auf Ihren Code

(und langsamer!).
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top