Kotlin kompilieren: JetBrains VS ANTLR VS JavaCC


    Wie schnell analysiert Kotlin und was macht das aus? JavaCC oder ANTLR? Ist der Quellcode von JetBrains geeignet?

    Vergleiche, fantasiere und wundere mich.

    tl; dr


    JetBrains sind zu schwer zu tragen, ANTLR ist hyip, aber unerwartet langsam, und JavaCC ist noch zu früh, um es abzuschreiben.

    Analysieren einer einfachen Kotlin-Datei mit drei verschiedenen Implementierungen:
    ИмплементацияПервый запуск1000й запускразмер джара (парсера)
    JetBrains3254мс16,6мс35.3МБ
    JetBrains (w/o analyzer)1423мс0,9мс35.3МБ
    ANTLR3705мс137,2мс1.4МБ
    JavaCC19мс0,1мс0.8МБ

    Ein ruhiger sonniger Tag ...


    Ich entschied mich, einen Übersetzer aus einer bequemen Sprache in GLSL zu integrieren. Die Idee war, die Shader direkt in die Idee zu programmieren und "kostenlose" Unterstützung für IDE zu erhalten - Syntax, Debugging und Komponententests. Es stellte sich als sehr praktisch heraus .

    Seitdem ist die Idee geblieben, Kotlin zu verwenden - Sie können den Namen vec3 verwenden, er ist strenger und praktischer für die IDE. Darüber hinaus ist es HYIP. Aus der Sicht meines internen Managers sind dies alles unzureichende Gründe, aber die Idee kam so oft zurück, dass ich mich entschlossen habe, sie einfach durch Implementierung loszuwerden.

    Warum nicht Java? Es gibt keine Operatorüberladung, daher unterscheidet sich die Syntax der Vektorarithmetik zu sehr von dem, was Sie im Spiel dev gewohnt sind

    JetBrains


    Die Jungs von JetBrains haben den Code für ihren Compiler auf den Githab gesetzt . Wie man es benutzt, kann man hier und hier ausspionieren .

    Zuerst habe ich ihren Parser zusammen mit dem Analysator verwendet, denn um in eine andere Sprache zu übersetzen, muss man wissen, welchen Typ die Variable hat, ohne den Typ explizit anzugeben val x = vec3(). Hier ist der Typ für den Leser offensichtlich, aber in AST sind diese Informationen nicht so einfach zu erhalten, insbesondere wenn rechts eine andere Variable oder ein Funktionsaufruf vorhanden ist.

    Hier wurde ich enttäuscht. Der erste Durchlauf des Parsers für eine primitive Datei dauert 3 Sekunden (DREI SEKUNDEN). Diese Zeit hat folgende offensichtliche Unannehmlichkeiten:

    Kotlin JetBrains parser
    first call elapsed : 3254.482ms
    min time in next 10 calls: 70.071ms
    min time in next 100 calls: 29.973ms
    min time in next 1000 calls: 16.655ms
    Whole time for 1111 calls: 40.888756 seconds



    1. weil es plus drei Sekunden dauert, um ein Spiel oder eine Anwendung zu starten.
    2. Während der Entwicklung verwende ich HotShader-Überladung und sehe das Ergebnis unmittelbar nach dem Ändern des Codes.
    3. Ich starte die Anwendung oft neu und bin froh, dass sie schnell genug startet (zweite oder zweite).

    Plus drei Sekunden, um den Parser aufzuwärmen - das ist inakzeptabel. Natürlich wurde sofort klar, dass die Parsing-Zeit bei nachfolgenden Aufrufen auf 50 ms und sogar auf 20 ms sinkt, wodurch (fast) die Unannehmlichkeiten Nr. 2 aus dem Ausdruck entfernt werden. Aber die anderen beiden gehen nirgendwohin. Zusätzlich sind 50 ms pro Datei plus 2500 ms pro 50 Dateien (ein Shader ist 1-2 Dateien). Und wenn das Android ist? (Hier reden wir bisher nur über die Zeit.) Die

    verrückte Arbeit von JIT macht auf sich aufmerksam. Die Parsingzeit einer einfachen Datei fällt von 70 ms auf 16 ms. Das bedeutet zum einen, dass JIT selbst Ressourcen verbraucht, und zum anderen kann das Ergebnis für eine andere JVM sehr unterschiedlich sein.

    Um herauszufinden, woher diese Zahlen kommen, habe ich eine Option gefunden, ihren Parser ohne Analysegerät zu verwenden. Schließlich muss ich nur die Typen sortieren, und das ist relativ einfach möglich, während der JetBrains-Analysator etwas komplizierter macht und viel mehr Informationen sammelt. Und dann fällt die Startzeit zweimal ab (aber fast eine halbe Sekunde ist immer noch anständig), und die Zeit der nachfolgenden Anrufe ist viel interessanter - von 8 ms in den ersten zehn auf 0,9 ms, irgendwo in tausend.

    Kotlin JetBrains parser (without analyzer) (исходник)
    first call elapsed : 1423.731ms
    min time in next 10 calls: 8.275ms
    min time in next 100 calls: 2.323ms
    min time in next 1000 calls: 0.974ms
    Whole time for 1111 calls: 3.6884801 seconds

    Ich musste gerade solche Zahlen sammeln. Die Zeit des ersten Durchlaufs ist wichtig, wenn die ersten Shader geladen werden. Dies ist kritisch, da Sie den Benutzer nicht ablenken, während der Shader im Hintergrund geladen wird. Er wartet nur. Der Rückgang der Ausführungszeit ist wichtig, um die Dynamik selbst zu sehen, wie die JIT funktioniert und wie effektiv wir den Shader in die aufgewärmte Anwendung laden können.

    Der Hauptgrund für den ersten Blick auf den JetBrains-Parser war die Verwendung der Schreibmaschine. Sobald die Zurückweisung zu einer diskutierten Option wird, können Sie andere Parser verwenden. Nicht-JetBrains sind außerdem wahrscheinlich viel kleiner, weniger umweltfreundlich, einfacher zu unterstützen und fügen Code in das Projekt ein.

    ANTLR


    Es gab keinen Parser auf JavaCC, aber auf dem HYIP ANTLR, wie erwartet, gibt es ( eins , zwei ).

    Unerwartet war Geschwindigkeit. Gleiche 3c zum Laden (erster Anruf) und fantastische 140ms für nachfolgende Anrufe. Der erste Start dauert nicht nur unangenehm lange, sondern die Situation wird nicht korrigiert. Anscheinend haben die Jungs von JetBrains etwas Magisches erlebt und JIT ihren Code so sehr optimieren lassen. Weil ANTLR mit der Zeit überhaupt nicht optimiert wird.

    Kotlin ANTLR parser(исходник)
    first call elapsed : 3705.101ms
    min time in next 10 calls: 139.596ms
    min time in next 100 calls: 138.279ms
    min time in next 1000 calls: 137.20099ms
    Whole time for 1111 calls: 161.90619 seconds

    JavaCC


    Im Allgemeinen sind wir überrascht, ANTLR-Dienste abzulehnen. Das Parsen sollte nicht so lange dauern! In der Kotlin-Grammatik gibt es keine kosmischen Unklarheiten, und ich habe sie auf fast leere Dateien geprüft. Es ist also an der Zeit, das alte JavaCC aufzudecken, die Ärmel hochzukrempeln und trotzdem "es selbst zu machen und wie Sie sollten".

    Dieses Mal waren die Zahlen zu erwarten, obwohl sie im Vergleich zu Alternativen unerwartet angenehm waren. Plötzliche Vorteile meines Parsers auf JavaCC Anstatt meinen Parser zu schreiben, möchte ich natürlich eine fertige Lösung verwenden. Die bestehenden haben jedoch große Nachteile: - Leistung (Pausen beim Lesen eines neuen Shatters sind inakzeptabel, sowie drei Sekunden Aufwärmen beim Start)

    Kotlin JavaCC parser (исходник)
    first call elapsed : 19.024ms
    min time in next 10 calls: 1.952ms
    min time in next 100 calls: 0.379ms
    min time in next 1000 calls: 0.114ms
    Whole time for 1111 calls: 0.38707677 seconds





    - ein riesiger Laufzeitläufer, ich bin mir nicht mal sicher, ob der Parser damit in das Endprodukt verpackt werden kann
    - übrigens ist die aktuelle Lösung mit Groovy dasselbe Problem - die Laufzeit dehnt sich aus,

    während der resultierende Parser auf JavaCC

    sowohl beim Start als auch beim Start eine große Geschwindigkeit aufweist Prozess
    + nur wenige Klassen des Parsers selbst

    Schlussfolgerungen


    JetBrains sind zu schwer zu tragen, ANTLR ist hyip, aber unerwartet langsam, und JavaCC ist noch zu früh, um es abzuschreiben.

    Eine einfache Kotlin-Datei mit drei verschiedenen Implementierungen analysieren: Irgendwann entschied ich mich, die Größe der Dose mit allen Abhängigkeiten zu betrachten. JetBrains sind wie erwartet großartig, aber die ANTLR-Laufzeit überrascht in ihrer Größe . UPDATE: Anfangs habe ich 15 MB geschrieben, aber wenn Sie die antlr4-runtime anstelle von antlr4 anschließen, fällt die Größe auf den erwarteten Wert. Obwohl der JavaCC-Parser selbst 10 Mal kleiner ist als ANTLR (wenn Sie den gesamten Code mit Ausnahme der Parser selbst entfernen).

    ИмплементацияПервый запуск1000й запускразмер джара (парсера)
    JetBrains3254мс16,6мс35.3МБ
    JetBrains (w/o analyzer)1423мс0,9мс35.3МБ
    ANTLR3705мс137,2мс1.4МБ
    JavaCC19мс0,1мс0.8МБ



    Die Größe des Gefäßes selbst ist natürlich für Mobiltelefone wichtig. Es ist jedoch auch für den Desktop von Bedeutung, da es tatsächlich die Menge an zusätzlichem Code bedeutet, in dem Fehler gefunden werden können, die von der IDE indiziert werden müssen, was sich tatsächlich auf die Geschwindigkeit des ersten Ladens und die Aufwärmgeschwindigkeit auswirkt. Darüber hinaus besteht bei komplexem Code keine besondere Hoffnung, in eine andere Sprache zu senden.
    Ich rufe nicht zum Zählen von Kilobytes auf, und ich schätze die Zeit und die Bequemlichkeit des Programmierers, aber es lohnt sich, über das Sparen nachzudenken, denn so werden Projekte schleppend und schwer zu warten.

    Noch ein paar Worte zu ANTLR und JavaCC

    Ein wichtiges Merkmal von ANTLR ist die Trennung von Grammatik und Code. Es wäre gut, wenn Sie nicht so viel dafür bezahlen müssten. Ja, und dies ist nur für "Entwickler der seriellen Grammatik" wichtig, und für Endprodukte ist dies nicht so wichtig, da selbst die vorhandene Grammatik noch durchlaufen werden muss, um eigenen Code zu schreiben. Wenn wir die Grammatik eines Drittanbieters speichern und übernehmen, kann dies einfach unpraktisch sein. Sie muss dennoch gründlich verstanden werden, um den Baum für sich selbst zu transformieren. Im Allgemeinen mischt JavaCC natürlich Fliegen und Burger, aber spielt es eine Rolle und fühlt es sich schlecht an?

    Ein weiterer Zähler von ANTLR ist die Menge der Zielplattformen. Aber hier kann man von der anderen Seite sehen - der Code unter JavaCC ist sehr einfach. Und es ist sehr einfach ... zu senden! Direkt mit Ihrem benutzerdefinierten Code - auch in C #, sogar in JS.

    PS


    Der gesamte Code ist hier github.com/kravchik/yast

    Das Ergebnis der Analyse ist ein Baum, der auf YastNode aufgebaut ist (dies ist eine sehr einfache Klasse, in der Tat eine Karte mit praktischen Methoden und einer ID). YastNode ist jedoch nicht gerade ein „sphärischer Knoten im Vakuum“. Es ist diese Klasse, die ich aktiv benutze. Basierend darauf habe ich verschiedene Werkzeuge zusammengestellt - einen Typifizierer, mehrere Übersetzer und einen Optimierer / Inliner.

    Der JavaCC-Parser enthält noch nicht die gesamte Grammatik, es sind noch 10 Prozent übrig, aber es scheint nicht, dass sie die Leistung beeinträchtigen könnten - ich habe die Geschwindigkeit überprüft, als Regeln hinzugefügt wurden, und es hat sich nicht merklich geändert. Außerdem habe ich bereits viel mehr getan, als ich brauchte, und versuche nur, das unerwartete Ergebnis des Prozesses mitzuteilen.

    Jetzt auch beliebt: