2016-12-24 16 views
2

Mein Verständnis der TCL-Ausführung ist, wenn die Compilerfunktion eines Befehls definiert ist, wird es zuerst aufgerufen, wenn es darum geht, den Befehl auszuführen, bevor seine Ausführungsfunktion aufgerufen wird.Wann wird die Kompilierfunktion eines Befehls aufgerufen?

Nehmen Sie das Kommando als Beispiel anhängen, hier ist seine Definition in tclBasic.c:

static CONST CmdInfo builtInCmds[] = { 

    {"append",   (Tcl_CmdProc *) NULL, Tcl_AppendObjCmd, 
     TclCompileAppendCmd,   1}, 

Hier ist mein Testskript:

$ cat t.tcl 
set l [list 1 2 3] 
append l 4 

ich hinzufügen gdb an beiden Funktionen Haltepunkte, TclCompileAppendCmd und Tcl_AppendObjCmd . Meine Erwartung ist TclCompileAppendCmd wird vor Tcl_AppendObjCmd getroffen.

Gdb Ziel ist tclsh8.4 und Argument ist t.tcl.

Was ich sehe, ist interessant:

  1. TclCompileAppendCmd bekommen zuerst anstößt, aber es ist nicht von t.tcl, eher aus init.tcl ist.
  2. TclCompileAppendCmd wird mehrmals getroffen und alle stammen von init.tcl.
  3. Das erste Mal t.tcl ausgeführt wird, ist es Tcl_AppendObjCmd wird getroffen, nicht TclCompileAppendCmd.

Ich kann nicht Sinn geben:

  1. Warum ist die Funktion für init.tcl genannt Kompilierung aber nicht für t.tcl?

  2. Jedes Skript sollte unabhängig kompiliert werden, d. H. Das Objekt mit dem kompilierten Befehl append bei init.tcl wird nicht für spätere Skripts wiederverwendet, oder?

[UPDATE] Dank Brad für die Spitze, nachdem ich das Drehbuch zu einem proc bewegen, kann ich sehen, TclCompileAppendCmd getroffen wird.

+1

Versuchen Sie, Ihren Test innerhalb eines Proc. Q2 hat eine falsche Annahme. Der Befehl 'append' ist kompiliert. –

+1

Es ist eine der Eigenarten des Tcl-Bytecode-Compilers. Im Allgemeinen, sobald ein Skript langsam wird, erstellen Sie eine Hauptprozedur und fügen alles ein, damit es kompiliert wird. –

+0

Die Ausnahme sind die sehr großen Skripten in EDA-Tools gefunden. Jene stoßen auf schreckliche Speichergrenzen ... –

Antwort

0

Die Kompilierungsfunktion (TclCompileAppendCmd in Ihrem Beispiel) wird vom Bytecode-Compiler aufgerufen, wenn sie Bytecode für eine bestimmte Instanz dieses bestimmten Befehls ausgeben möchte. Der Bytecode-Compiler hat auch einen Fallback, wenn es keine Kompilierungsfunktion für einen Befehl gibt: Er gibt Anweisungen aus, um die Standardimplementierung aufzurufen (was in diesem Fall Tcl_AppendObjCmd wäre; die NULL in dem anderen Feld bewirkt, dass Tcl einen Thunk erzeugt, falls jemand wirklich besteht darauf, eine bestimmte API zu verwenden, aber Sie können das ignorieren). Das ist ein nützliches Verhalten, weil Operationen wie I/O behandelt werden; Der Aufwand für das Aufrufen einer Standardbefehlsimplementierung ist im Vergleich zum Aufwand für das Ausführen von Festplatten- oder Netzwerk-E/A ziemlich gering.

Aber wann läuft der Bytecode-Compiler?

Auf einer Ebene wird es ausgeführt, wenn der Rest von Tcl fragt, ob es ausgeführt werden soll. Einfach! Aber das hilft dir nicht wirklich.Genauer gesagt läuft es immer dann, wenn Tcl einen Skriptwert in einem Tcl_Obj auswertet, der noch keinen Bytecode-Typ hat (oder wenn der gespeicherte Bytecode angibt, dass es für einen anderen Auflösungskontext oder eine andere Kompilierungsepoche ist) außer der Auswertung hat gebeten, nicht Bytecode von der Flagge TCL_EVAL_DIRECT bis Tcl_EvalObjEx oder Tcl_EvalEx kompiliert werden (das ist ein praktischer Wrapper für Tcl_EvalObjEx). Es ist diese Flagge, die dir Probleme verursacht.

Wann wird dieses Flag verwendet?

Es ist eigentlich ziemlich einfach: Es wird verwendet, wenn einige Code nur einmal ausgeführt werden soll, weil dann die Kosten der Kompilierung größer als die Kosten der Verwendung des Interpretationspfades sind. Es ist besonders von der Tk bind Befehl für substituierte Skript Rückrufe ausgeführt wird, aber es ist auch von source und der Hauptcode von tclsh (im Wesentlichen nichts mit Tcl_FSEvalFileEx oder seinen Vorgängern/Wrapper Tcl_FSEvalFile und Tcl_EvalFile) verwendet wird. Ich bin mir nicht 100% sicher, ob das die richtige Wahl für einen source d Kontext ist, aber es passiert jetzt was. Es gibt jedoch eine Problemumgehung, die (sehr!) Sinnvoll ist, wenn Sie Schleifen behandeln: Sie können den Code in einem kompilierten Kontext innerhalb dieser source mit einer Prozedur, die Sie sofort aufrufen, oder verwenden Sie eine apply (ich empfehle letzteres in diesen Tagen). init.tcl verwendet diese Tricks, weshalb Sie sehen, dass es Dinge kompiliert.

Und nein, wir speichern normalerweise keine kompilierten Skripts zwischen Läufen von Tcl. Unser Compiler ist schnell genug, dass es sich nicht wirklich lohnt; Die Kosten für die Überprüfung, ob der geladene kompilierte Code für den aktuellen Interpreter korrekt ist, sind hoch genug, um schneller aus dem Quellcode zu kompilieren. Unser aktueller Compiler ist schnell (Ich arbeite an einem langsameren, der enorm besseren Code generiert). Es gibt eine kommerzielle Tool-Suite von ActiveState (das Tcl Dev Kit), die einen Compiler vor der Zeit enthält, der sich jedoch auf die Verschleierung von Code für kommerzielle Zwecke und nicht auf Geschwindigkeit konzentriert.

+0

Meine Antwort bezieht sich streng auf 8.5 und 8.6, ist aber ziemlich übertragbar auf 8.4 –

Verwandte Themen