Kann ein Compiler reine Funktionen ohne die Typinformationen zur Reinheit automatisch erkennen?

Lesezeit: 7 Minuten

Benutzeravatar von Christian Zeller
Christian Zeller

Ich argumentiere also mit meinem Freund, der behauptet, dass ein Compiler wie GCC eine reine Funktion automatisch ohne Typinformationen erkennen kann. Ich bezweifle das.

Sprachen wie D oder Haskell haben Reinheit in ihren Typsystemen und ein Programmierer definiert explizit, welche Funktion rein ist oder nicht. Eine reine Funktion hat keine Nebenwirkungen und kann daher sehr einfach parallelisiert werden.

Die Frage ist also: Ist das alles nötig oder nicht? Könnte ein Compiler Reinheit ohne Meta- oder Typinformationen erkennen, indem er einfach annimmt, dass alles, was IO ausführt oder automatisch auf globale Variablen zugreift, nicht rein ist?

  • Nun, beide Antworten wären interessant – wenn es theoretisch möglich wäre und wenn irgendein produktionsreifer Compiler so etwas tut.

    – Christian Zeller

    6. Januar 2012 um 16:27 Uhr


  • Außerdem stellt sich die Frage, ob Compiler wäre in der Lage reine Funktionen zu erkennen oder ob Compiler eigentlich gelten so ein Erkennungsalgorithmus?

    – Niklas B.

    6. Januar 2012 um 16:28 Uhr


  • Gäbe es keine getrennte Kompilierung, könnte C ein perfektes Wissen darüber haben, was rein und unrein ist. Nur weil diese Informationen nicht als “Typ” bezeichnet werden, heißt das nicht, dass ein Compiler sich dessen nicht bewusst sein kann. Das Problem mit separater Kompilierung – Funktionen, die aus Objektdateien oder was auch immer verknüpft sind, enthalten keine Reinheitsmetadaten. Selbst wenn dies der Fall wäre, sind diese Informationen erst nach dem Linken verfügbar, sodass der Compiler sie nicht sieht. Innerhalb einer bestimmten Quelldatei kann der Compiler jedoch Funktionen identifizieren, die keine mögliche Verunreinigungsquelle haben, und kann diese Informationen für bestimmte Optimierungen verwenden.

    Benutzer180247

    20. Januar 2012 um 2:53 Uhr

ehirds Benutzeravatar
dritte

Sicher, man kann in manchen Fällen reine Funktionen erkennen. Zum Beispiel,

int f(int x)
{
    return x*2;
}

kann mit einfacher statischer Analyse als rein nachgewiesen werden. Die Schwierigkeit besteht darin, dies im Allgemeinen zu tun, und das Erkennen von Schnittstellen, die einen “internen” Zustand verwenden, aber extern rein sind, ist im Grunde unmöglich.

GCC hat die Warnmöglichkeiten -Wsuggest-attribute=pure und -Wsuggest-attribute=constdie Funktionen vorschlagen, die Kandidaten für die sein könnten pure und const Attribute. Ich bin mir nicht sicher, ob es sich dafür entscheidet, konservativ zu sein (dh viele reine Funktionen zu vermissen, es aber nie für eine nicht reine Funktion vorzuschlagen) oder den Benutzer entscheiden lässt.

Beachten Sie, dass die GCC-Definition von pure ist “nur von Argumenten und globalen Variablen abhängig”:

Viele Funktionen haben außer dem Rückgabewert keine Auswirkungen und ihr Rückgabewert hängt nur von den Parametern und/oder globalen Variablen ab. Eine solche Funktion kann genau wie ein arithmetischer Operator der Eliminierung gemeinsamer Unterausdrücke und der Schleifenoptimierung unterzogen werden. Diese Funktionen sollten mit dem Attribut deklariert werden pure.

GCC-Handbuch

Strikte Reinheit, dh gleiche Ergebnisse für gleiche Argumente unter allen Umständen, wird durch die repräsentiert const -Attribut, aber eine solche Funktion kann nicht einmal einen an sie übergebenen Zeiger dereferenzieren. Also die Parallelisierungsmöglichkeiten z pure Funktionen sind begrenzt, aber viel weniger Funktionen können es sein const im Vergleich zu den reinen Funktionen kann man in einer Sprache wie Haskell schreiben.

Übrigens ist die automatische Parallelisierung reiner Funktionen nicht so einfach, wie Sie vielleicht denken; der schwierige Teil wird entscheiden was zu parallelisieren. Parallelisieren Sie Berechnungen, die zu billig sind, und Overhead macht es sinnlos. Wenn Sie nicht genug parallelisieren, profitieren Sie nicht von den Vorteilen. Ich kenne keine praktische funktionale Sprachimplementierung, die aus diesem Grund eine automatische Parallelisierung durchführt, obwohl Bibliotheken dies mögen Repa Parallelisieren Sie viele Operationen hinter den Kulissen ohne explizite Parallelität im Benutzercode.

  • Außerdem wird für Sprachen wie C und C++ erwartet, dass der Compiler eine Assembly erzeugt, die ziemlich genau die Struktur des angegebenen Programms widerspiegelt, daher glaube ich nicht, dass es erlaubt wäre, Optimierungen anzuwenden, die so aufdringlich sind wie die automatische Parallelisierung. Wissen Sie übrigens, ob GCC eine solche Erkennung anwendet? Wenn ja, zu welchem ​​Zweck?

    – Niklas B.

    6. Januar 2012 um 16:31 Uhr


  • @NiklasBaumstark: C hat die “Als-ob”-Regel, die es der Implementierung erlaubt, alles zu tun, was sie will, solange ein konformes Programm das Ergebnis nicht unterscheiden kann. Unter diesen Schirm würde die Parallelisierung der Ausführung reiner Funktionen fallen.

    – 3

    6. Januar 2012 um 16:32 Uhr

  • Ich habe gerade meine Antwort erweitert, um die GCC-Frage zu beantworten.

    – 3

    6. Januar 2012 um 16:32 Uhr

  • Ein kluger Compiler wird beim Erraten der “Reinheit” einer Funktion (aus Gründen der Optimierung) falsch negative Ergebnisse zulassen (möglicherweise verpasst er Chancen zur Optimierung), aber keine falsch positiven Ergebnisse (andernfalls würde eine fehlerhafte Optimierung durchgeführt).

    – Dan Burton

    6. Januar 2012 um 17:44 Uhr

  • @ehird: Das stimmt, aber wenn ein C-Compiler automatisch Threads hinter Ihrem Rücken erstellt, würde sich niemand ihm nähern.

    – Peter Alexander

    6. Januar 2012 um 18:12 Uhr

Benutzeravatar von Ingo
Ingo

Es gibt ein weiteres Problem. In Betracht ziehen

int isthispure(int i) {
   if (false) return getchar();
   return i + 42;
}

Die Funktion ist effektiv rein, obwohl sie unreinen Code enthält, aber dieser Code kann nicht erreicht werden. Nun nehme an false wird ersetzt durch g(i) aber wir wissen ziemlich sicher, dass g(i) falsch ist (zum Beispiel könnte g prüfen, ob sein Argument a ist Lychrel-Nummer). Um zu beweisen, dass Isthispur tatsächlich rein ist, müsste der Compiler beweisen, dass keine Lychrel-Zahlen existieren.

(Ich gebe zu, dass dies eine ziemlich theoretische Überlegung ist. Man könnte auch entscheiden, dass eine Funktion, die einen unreinen Code enthält, selbst unrein ist. Dies wird jedoch meiner Meinung nach nicht durch das C-Typ-System gerechtfertigt.)

  • +1, aber eigentlich müsste der Compiler nur prüfen, ob die Ganzzahlen reichen INT_MIN zu INT_MAX sind keine Lycrel-Nummern, was eine viel einfachere Aufgabe ist, als zu beweisen, dass keine existieren. Ein guter statischer Analysator könnte sicherlich einen Brute-Force-Ansatz auf Funktionen anwenden, deren Argumentraum kleiner als eine bestimmte Grenze ist M (z.B M=1<<32 oder so).

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    6. Januar 2012 um 17:45 Uhr

  • Nicht einmal Haskell exakt behaupten, dass es eindeutige Reinheit oder Unreinheit gibt. Wenn eine Funktion verwendet unsafePerformIO, der Autor kann sich irren, dass nichts Unreines vorkommt. Außerdem gibt es Funktionen mit Rückgabetyp IO a die keine IO ausführen.

    – amndfv

    6. Januar 2012 um 19:07 Uhr

  • @amindfv: Nun, unsafePerformIO ist nicht “wirklich” Teil von Haskell, auch wenn es ein Standardteil des FFI ist.

    – 3

    6. Januar 2012 um 21:11 Uhr

  • @R. Ich wette, Sie können das nicht für nur 10 Ints (sagen wir von 190 bis 200) in angemessener Zeit tun. Es gibt Lycrel-Kandidaten (war es 192 oder 196, keine Ahnung im Moment), bei denen Ihr PC vielleicht ein Jahr oder so beschäftigt ist und immer noch nicht bewiesen hat, dass er keiner ist.

    – Ing

    6. Januar 2012 um 22:16 Uhr

  • Auf der anderen Seite von unsafePerformIO gibt es IO-Aktionen, die sich als völlig rein herausstellen. Zum Beispiel return 0 – der Typ ist IO Int (oder IO Num oder was auch immer), aber es gibt keine Effekte. Dies ist dem C-Beispiel für diese Frage sehr ähnlich – insbesondere wenn wir einige wirklich effektive Aktionen hinzufügen, die niemals verwendet werden, wie z if False then getChar else (return ' '). Haskell-Code kann effektiv erscheinen, aber auch keine Auswirkungen haben – es ist nur so, dass die Täuschung eher explizit als implizit im Typ ist.

    Benutzer180247

    20. Januar 2012 um 2:43 Uhr


Die Bestimmung, ob eine Funktion rein ist (selbst in dem von GCC verwendeten begrenzten Sinne), entspricht dem Halteproblem, daher lautet die Antwort “nicht für beliebige Funktionen”. Es ist möglich, automatisch zu erkennen, dass einige Funktionen rein und andere nicht rein sind, und den Rest als „unbekannt“ zu kennzeichnen, was in einigen Fällen eine automatische Parallelisierung ermöglicht.

Meiner Erfahrung nach sind selbst Programmierer nicht sehr gut darin, solche Dinge herauszufinden, also möchte ich, dass das Typsystem dabei hilft, den Überblick zu behalten Für michnicht nur für den Optimierer.

Ich entdeckte beim Schreiben eines Artikels Vergleich der Leistung von C# und C++ dass Visual C++ tatsächlich eine reine Funktion mittlerer Komplexität erkennen kann, während a aufgerufen wird Funktion, die ein Polynom berechnet.

Ich habe die Polynomfunktion in einer Schleife aufgerufen, um Zeit auf der Uhr zu verschlingen. Der Compiler hat den Aufruf so optimiert, dass er einmal ausgeführt wird, bevor die Schleife gestartet wird, und das Ergebnis innerhalb der Schleife wiederverwendet. Dazu müsste es wissen, dass es keine Nebenwirkungen gibt.

Ich muss aber sagen, es ist schön, dazu in der Lage zu sein Garantie dass der Compiler eine Optimierung vornehmen kann, indem er eine Funktion als rein markiert, und es dient auch als Form der Dokumentation.

1396850cookie-checkKann ein Compiler reine Funktionen ohne die Typinformationen zur Reinheit automatisch erkennen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy