2012-04-11 8 views
31

Ich habe vor kurzem entdeckt, dass man JIT (just in time) Kompilierung mit R mit dem Compiler-Paket verwenden kann (ich fasse meine Erkenntnisse zu diesem Thema in a recent blog post).Mögliche Mängel bei der Verwendung von JIT mit R?

Eine der Fragen wurde ich gefragt ist:

Gibt es eine Gefahr? es klingt zu gut, um wahr zu sein, setzen Sie einfach eine Zeile Code und das ist es.

Nachdem ich mich umgesehen habe, könnte ich ein mögliches Problem finden, das mit der "Start" -Zeit für das JIT zu tun hat. Aber gibt es noch ein anderes Problem bei der Verwendung von JIT?

Ich vermute, dass es mit der Umgebungsarchitektur von R einige Einschränkungen geben wird, aber ich kann mir keine einfache Illustration des Problems vorstellen, irgendwelche Vorschläge oder rote Flaggen werden eine große Hilfe sein?

+0

Ich bin mir nicht sicher über Performance-Hits (andere als erste Compilations (und vielleicht erhöhte Speicherauslastung)), aber die "Hinweis: keine sichtbare Bindung" Nachrichten können oft für einen Neuling überwältigend sein (z. B. wenn ggplot2) Tab-Complete (zumindest sind sie für mich) – mweylandt

+0

Hallo mweylandt. Weißt du zufällig, was diese Fehlermassage bedeutet? –

+4

Ich habe 'ByteCompile: true' in die DESCRIPTION-Datei meiner Pakete gesetzt, als ich neue Versionen erstelle und es scheint in Ordnung zu sein. Ich habe einen kleinen Test "http://www.johnmyleswhite.com/notebook/2012/03/31/julia-ich-liebe-du/kommentar-seite-1/# kommentar-19522" und die Byte kompilierte Version, ' fib2c lief 4x schneller als das normale fib2a. In einigen Fällen ist R sogar ohne Byte-Kompilierung bereits schnell (z. B. stark vektorisierter Code, der C darunter verwendet), und in diesen Fällen gibt es offensichtlich wenig Gelegenheit zur Beschleunigung - es ist hauptsächlich für langsamen R-Code nützlich. –

Antwort

4

Das rpart oben angegebene Beispiel, scheint nicht mehr ein Problem zu sein:

library("rpart") 
fo = function() { 
    for(i in 1:500){ 
    rpart(Kyphosis ~ Age + Number + Start, data=kyphosis) 
    } 
} system.time(fo()) 
# user system elapsed 
# 1.212 0.000 1.206 
compiler::enableJIT(3) 
# [1] 3 
system.time(fo()) 
# user system elapsed 
# 1.212 0.000 1.210 

Ich habe versucht, auch eine Reihe von anderen Beispielen wie

  • einen Vektor wächst;
  • Eine Funktion, um mean

nur ein Wrapper ist Während ich nicht immer eine Geschwindigkeit-up bekommen, habe ich erfahren, nie signifikant eine Verlangsamung.


R> sessionInfo() 
R version 3.3.0 (2016-05-03) 
Platform: x86_64-pc-linux-gnu (64-bit) 
Running under: Ubuntu 16.04 LTS 
11

die Ausgabe eines einfachen Test mit rpart könnte eine Beratung sein nicht enableJIT in allen Fällen zu verwenden:

library(rpart) 
fo <- function() for(i in 1:500){rpart(Kyphosis ~ Age + Number + Start, data=kyphosis)} 
system.time(fo()) 
#User  System verstrichen 
#2.11  0.00  2.11 

require(compiler) 
enableJIT(3) 
system.time(fo()) 
#User  System verstrichen 
#35.46  0.00  35.60 

Alle explanantion?

+1

Das ist komisch, also ist etwas, das die Schleife in fo kompiliert, ein Problem verursacht. Wenn Sie es normalerweise kompilieren, wird es nicht passieren. http://ideone.com/Nu8IZ, Hinweis rpart ist bereits Byte kompiliert. – Hansi

+7

Kompilieren dauert eine gute halbe Minute: Ich sehe das gleiche (2,8 s - 42,6 s), aber dann macht system.time (fo()) wieder nur 2,6 s. – cbeleites

+1

Ich vermute, dass dies ein unerwartetes Verhalten ist ... –

-2

Weiter zu der vorherigen Antwort, Experimente zeigen das Problem ist nicht mit der Kompilation der Schleife, es ist mit der Zusammenstellung von Verschlüsse. [enableJIT (0) oder enableJIT (1) lassen den Code schnell, enableJIT (2) verlangsamt ihn drastisch und enableJIT (3) ist etwas schneller als die vorherige Option (aber immer noch sehr langsam)]. Im Gegensatz zu Hansis Kommentar verlangsamt cmpfun die Ausführung in ähnlichem Maße.

+0

Es scheint klar, dass der Grund für unterschiedliche Ergebnisse verschiedene Versionen von R waren. Verwenden von R 3.3.2 heute, enableJIT (i) gibt genau das gleiche Timing (1,21 Sekunden) unabhängig davon, ob ich 0, 1, 2 oder 3 bin. Ich überlasse die Interpretation anderen, aber die Nachricht an mich ist, es ist ohne irgendwelche optimiert Hilfe vom Benutzer jetzt. – Elroch

0

Grundsätzlich wäre nach der Byte-Code kompiliert und geladen wird, es sollte immer mindestens so schnell wie das Original AST-Interpreter interpretiert werden. Einige Codes profitieren von großen Beschleunigungen, dies ist normalerweise Code mit vielen skalaren Operationen und Schleifen, wo die meiste Zeit in R-Interpretation verbracht wird (Ich habe Beispiele mit 10-facher Beschleunigung gesehen, aber willkürliche Mikro-Benchmarks könnten dies bei Bedarf aufblasen). Ein Teil des Codes wird mit der gleichen Geschwindigkeit laufen, dies ist in der Regel gut vektorisiert und verbraucht daher fast keine Zeit für die Interpretation. Jetzt kann die Kompilierung selbst langsam sein. Daher kompiliert der Just-in-Time-Compiler jetzt keine Funktionen, wenn er rät, dass er sich nicht auszahlt (und die Heuristiken ändern sich im Laufe der Zeit, dies ist bereits in 3.4.x). Die Heuristiken erraten es nicht immer richtig, daher kann es Situationen geben, in denen sich die Kompilation nicht auszahlt. Typische problematische Muster sind Codegenerierung, Codemodifizierung und Manipulation von Bindungen von Umgebungen, die in Schliessungen erfasst sind.

Pakete können zum Installationszeitpunkt bytekompiliert werden, so dass die Kompilierungskosten (wiederholt) zur Laufzeit nicht bezahlt werden, zumindest für Code, der im Voraus bekannt ist. Dies ist jetzt der Standard in der Entwicklungsversion von R. Während das Laden von kompiliertem Code viel schneller ist als das Kompilieren, kann in manchen Situationen sogar Code geladen werden, der nicht ausgeführt wird, so dass es tatsächlich einen Overhead geben kann, aber insgesamt Vorkompilierung ist vorteilhaft. In letzter Zeit wurden einige Parameter des GC eingestellt, um die Kosten des Ladens von Code zu reduzieren, der nicht ausgeführt wird.

Meine Empfehlung für Paketexperten wäre die Verwendung der Standardwerte (die Just-in-Time-Kompilierung ist jetzt standardmäßig in den freigegebenen Versionen aktiviert, die Bytekompilierung zur Paketinstallationszeit ist jetzt in der Entwicklungsversion aktiviert). Wenn Sie ein Beispiel finden, in dem der Bytecode-Compiler nicht gut funktioniert, reichen Sie bitte einen Fehlerbericht ein (ich habe in früheren Versionen auch einen Fall gesehen, der rpart betrifft). Ich würde gegen Code-Generierung und Code-Manipulation und vor allem in Hot-Loops empfehlen. Dies umfasst das Definieren von Schließungen, das Löschen und Einfügen von Bindungen in Umgebungen, die von Schließungen erfasst werden. Auf keinen Fall sollte man eval(parse(text= in heißen Schleifen machen (und das war schon ohne Byte-Kompilierung schlecht). Es ist immer besser, Zweige zu verwenden, als dynamisch neue Verschlüsse (ohne Zweige) zu generieren. Außerdem ist es besser, Code mit Schleifen zu schreiben, als Code mit riesigen Ausdrücken (ohne Schleifen) dynamisch zu generieren. Mit dem Byte-Code-Compiler ist es jetzt oft ok, Loops zu schreiben, die auf Skalaren in R laufen (die Performance wird nicht so schlecht wie vorher sein, also könnte man öfter wegkommen, ohne zu C für die Performance-kritischen Teile zu wechseln) .

Verwandte Themen