C ++ Konstante faltet eine Schleife für Primzahlen

11

Nachdem wir uns die vorherigen Fragen angeschaut haben 1 , 2 Ich habe mich gefragt, ob ich den Compiler zwingen kann, eine konstante Faltung für den folgenden Code durchzuführen, der Primzahlen ausgibt.

%Vor%

Und ich baue es über:

%Vor%

Als Ergebnis sind ein paar:

%Vor%

Ich möchte Compiler zwingen, den Code ähnlich wie

zu betrachten %Vor%

oder

%Vor%

Aber das Lesen der Baugruppe zeigt, dass nichts passiert.

Ich habe sogar __builtin_expect verwendet, aber es hat nicht funktioniert.

Gibt es eine Möglichkeit, den Compileroptimierer zu zwingen, die for-Schleife zu lesen und den Vorteil zu nutzen, dass die Ausgabedaten bekannt sind?

Ich würde es gerne tun, ohne Template-Metaprogrammierung zu verwenden.

PS. Mein Ziel ist es, nur den Compiler zu testen und nicht eine effiziente Methode, um Primzahlen zu berechnen. Ich möchte nur meinen Freunden zeigen, wie viel C ++ Compiler mächtig ist.

Wenn die Trennung von is_prime wichtig ist, lege ich alles in den Hauptteil und es wurde kein Unterschied festgestellt:

%Vor%

Es gibt sogar ein weiteres Beispiel, das für den Compiler keine Entschuldigung bleibt:

%Vor%

Assembly:

%Vor%     
ar2015 17.02.2018, 08:28
quelle

1 Antwort

2

Es gibt ein grundlegendes Missverständnis der Compiler hier. Untersuchen wir das Programm, das Sie sehr sorgfältig geschrieben haben, und denken Sie darüber nach, was Sie vom Compiler für Sie erwarten.

Das Hauptmerkmal des Programms ist, dass es keine Eingabe benötigt, aber es gibt eine Ausgabe aus, indem es in cout schreibt. Beachten Sie, dass die Funktion is_prime kein Compiler intrinsisch ist; der Compiler behandelt es nur als eine andere Funktion. Das ist wichtig und ich werde später darauf zurückkommen.

Nun, wie würde der Compiler das Programm so umwandeln, wie Sie es beschrieben haben? Wie kann es so etwas tun? Das heißt, wie kann der Compiler diese beiden verschachtelten Schleifen in eine einfache Sequenz von Anweisungen umwandeln, die ganze Zahlen in cout schreiben? Die einzige Möglichkeit, dies zu tun, ist, das Programm auszuführen , um alle Werte herauszufinden, die in cout geschrieben werden müssen.

Das macht keinen Sinn, oder? Schauen wir uns das große Bild hier an und betrachten Sie alle Programme (oder Anweisungsfolgen), die die gleiche Charakteristik haben; diejenigen, die keine Eingaben machen, sondern Ausgaben ausgeben. Die Frage würde lauten: Warum führt der Compiler den Quellcode nicht aus und gibt nur Code aus, der die Ausgabewerte schreibt? Aus folgenden Gründen:

  • Was passiert, wenn das Programm zu lange dauert? Was ist, wenn es einen Fehler gibt, der es für eine unerwartete Zeit laufen lässt? Es gibt sogar keine Garantie, dass das Programm jemals aufhört . Was soll der Compiler genau machen?
  • Letztlich besteht der Zweck des Compilers nicht darin, den Quellcode auszuführen, sondern einen funktional äquivalenten nativen Code auszugeben, der potenziell optimiert ist. Wenn das Programm keine Eingaben (wie Ihren Code) macht, können Sie den Code einfach kompilieren und einmal ausführen, um die Ausgabe zu sehen. Der Code muss auf jeden Fall vom Compiler oder durch Ausführen der ausführbaren Binärdatei ausgeführt werden, und es wird genauso lange dauern. In beiden Fällen muss der Code kompiliert und ausgeführt werden. Eine solche Optimierung fügt also keinen echten Wert hinzu. Dies steht jedoch im Gegensatz zu Templates, die beabsichtigt vom Compiler zur Kompilierzeit auf regulären Code reduziert werden sollen. Darüber hinaus wäre die Interpretation ein besseres Ausführungsmodell für solche Programme. Sie wollen sich nicht mit der Code-Erstellung beschäftigen? Gehen Sie voran und verwenden Sie einen Python-Interpreter oder einen anderen Interpreter.
  • Die Implementierung einer solchen Optimierung kann im Allgemeinen schwierig oder unmöglich sein. Was wäre, wenn der Mechanismus zur Ausgabe von Outputs Nebenwirkungen hätte, die zukünftige Output-Werte ändern könnten? Der Compiler weiß nicht genau, was passiert, wenn Sie in cout schreiben. Der Standard-Ausgabestream kann auf etwas Seltsames umgeleitet werden. Programme, die keine Eingaben machen, sind daher für den Compiler nicht unbedingt einfacher zu optimieren.

Das heißt, einfache Codeabschnitte, die in sehr kurzer Zeit ausgewertet werden können, werden vom Compiler tatsächlich ausgewertet. Diese Optimierung wird als konstante Faltung bezeichnet. Codeabschnitte, die den Status des Programms nicht beeinflussen, können eliminiert werden, ohne sie auszuführen. Wenn Sie beispielsweise cout<<i<<endl; entfernt haben, wird der Compiler nur den Rest des Codes optimieren. Dies wird Beseitigung von toten Codes genannt. Compiler machen diese Optimierungen, weil sie vom Compiler auf systematische Weise durchgeführt werden können und weil sie sehr effektiv auf echten Codebasen sind.

Aber was passiert, wenn die is_prime -Funktion ein intrinsischer Compiler ist? In diesem Fall hätte der Compiler wahrscheinlich eine eingebaute Tabelle mit häufig verwendeten Primzahlen und eine sehr schnelle Implementierung des Primzahltests. Sie können dann vom Compiler erwarten, das loop in der Hauptfunktion mehrmals, vielleicht sogar vollständig, zu entpacken, wobei nur Ausgabeanweisungen enthalten sind, die im Wesentlichen die von Ihnen gewünschte Transformation ausführen.

    
Hadi Brais 18.02.2018 18:23
quelle

Tags und Links