[Python-de] Minimal Python, Nachsatz

Christian Tismer tismer at tismer.com
Sun Jan 5 23:43:06 EST 2003


holger krekel wrote:
> Christian Tismer wrote:

[Bytecode-Interpreter in Python]

...

>>Habe nochmal kurz nachgedacht -- eigentlich hast Du
>>ja recht. Natürlich kann man einen kompletten
>>Interpreter in Python schreiben und sich zunächst
>>um nichts weiter kümmern. Das würde dann als
>>Aufsatz von Standard-Python laufen.
> 
> 
> und waere - obwohl sehr langsam - ein gutes Testbett
> fuer den bytecode-level.  analog zu 
> 
> compile.c <-> pycodegen.py dann 
> ceval.c <-> pyeval.py 

...

> ein pyeval.py waere sicher instruktiv. Und es waere ein 
> gut releasebarer Meilenstein :-)

Ja, das wäre in der Tat ein guter Grundstock.

> Ich vermute zum beispiel, dass viele bytecodes rekursiv 
> sind, d.h. dass der bytecode einer bytecode-interpretation 
> sich letztlich selbst enthaelt.  Das gilt nicht nur fuer 
> sowas wie CALL_FUNCTION (dessen python interpretation mit 
> sicherheit einen CALL_FUNCTION bytecode beinhaltet), sondern 
> wahrscheinlich schon fuer LOAD_FAST.  eine naive 
> python-implementierung saehe vielleicht so aus:

Zunächst zum CALL_FUNCTION.
Wenn Du Dir ceval.c daraufhin anschaust, siehst
Du, daß dieser Opcode eine Unmenge von Sachen tut.
Da wird analysiert, was da eigentlich gerufen werden
soll, ob's eine Methode ist oder eine CFunction,
was für eine Parameterübergabe, und und und.
Dann werden die Parameter entsprechend übergeben,
und je nach Aufrufart wird ein neuer Frame ausgeführt,
oder eine C-Funktion gerufen.
Das müßte jetzt die Python-Implementierung alles
irgendwie übernehmen. CALL_FUNCTION kommt in dem
Kontext aber garnicht mehr vor. Klar, das drunterliegende
Python benutzt intern CALL_FUNCTION, aber der in Python
geschriebene Interpreter braucht etwas anderes, was den
früheren, in C geschriebenen Aufruf ersetzt.
Das *könnte*, im alten Stil, sowas sein wie eine C-Funktion,
die jetzt tatsächlich einen Frame evaluiert, indem
sie unseren in Python geschriebenen Interpreter rekursiv
aufruft.
Es *kann* aber auch ein ganz anderer Opcode sein, zum
Beispiel ein CREATE_FRAME, gefolgt von PUSH_FRAME,
gefolgt von EXEC_FRAME. Das wäre ein nicht-rekursiver
Call.

> 
>    ...
>    elif op==LOAD_FAST:
>         stack.append(fastnames[oparg])
>    ...
> 
> aber der bytecode fuer die codezeile "stack.append..."
> wuerde selbst 'LOAD_FAST' benoetigen.  Wenn wir 
> 'self-contained' werden wollen, muesste der obige 
> sourcecode auf etwas abgebildet werden was sich letztlich
> nicht selbst aufruft. Das duerfte ja in deine Stackless
> Philosophie passen :-)

Ok, zum LOAD_FAST:
Bei stack.append machst Du die Annahme, daß es sich
beim Stack eines Frames um eine Liste handelt.
Das kann man natürlich machen. Aus Sicht des
darunterliegenden C-Interpreters ist das kein Opcode,
sondern ein CALL_FUNCTION auf die push-Methode des
Listenobjekts, das den Stack emuliert.
Geht man noch tiefer und baut wirklich die Struktur
des Frames nach, dann ist es keine Liste, sondern
ein Array festgelegter Länge, denn die Stacktiefe
wird zur Compile-Time berechnet.
Dieses Frame-Array ist in Segmente unterteilt, zur
Aufnahme von Locals, Stack-Variablen, FreeVars etc.
Die Zugriffs-Operation wäre dann eher ein indiziertes
Zuweisen an Array-Elemente, mit Veränderung des
Stackpointers.

Worauf ich hinauswill: Die Opcodes, die der in Python
geschriebene Interpreter interpretiert, werden auf
etwas ziemlich anderes abgebildet, nämlich auf die
Instruktionen, mit denen die Datenstrukturen des
emulierten Interpreters modifiziert werden.
Das heißt wiederum, daß die minimal nötige C-Engine
im Wesentlichen gerade diese Zugriffe implementieren
muß.
Viel mehr ist es wahrscheinlich garnicht.

Was ist ein JUMP? Nun, man verändert die Speicherzelle,
die als Instruction Pointer benutzt wird.
Was ist ein CALL? Man pusht den alten Wert vorher auf
irgendeinen Stack und holt ihn später zurück.

Also wie ich es sehe, tauchen die Opcodes garnicht
in irgendeiner rekursiven Form wieder auf. Sie
werden einfach aufgelöst in simple Speicheroperationen.

>>Vielleicht macht es Sinn, wenn dieser Interpreter
>>wirklich *alles* implementiert.
> 
> macht schon Sinn, ist nur etwas unabsehbar. IOW
> eignet sich nicht als Meilenstein :-)

Meinte ich anders.
Der Interpreter implementiert einfach alle Opcodes.
Dazu baut er alle notwendigen Strukturen nach, indem
er z.B. eine "class Frame" definiert, alles Nötige
dranhängt, und Programme damit ablaufen läßt.

Die Frage ist lediglich, wieviel er emulieren soll.
Im ersten Schritt könntest Du Listen und Dicts einfach
"klauen", das heißt, Du reichst die existierenden
Listen und Dicts einfach als solche durch und benutzt
sie direkt.
Unter "alles implementieren" verstehe ich, wenn man
noch weiter geht, und eben auch diese Listen und Dicts
selber als Klassen definiert, einschließlich aller
Hashing-Algorithmen, Index-Rechnung für den Zugriff
usw. Dies würde bedeuten, daß man lediglich Unterstützung
für einfache Arrays in C implementiert und alles andere
in Python obendrauf setzt.
Ich weißnicht, ob dies zu weit geht, oder ob Armin
Rigo genau sowas haben will, weil er dann Code
generieren kann ohne Ende.

...

> Mir erscheint vielversprechend, pyeval.py zu schreiben 
> und dass dann auf einem minimalisierten ceval.c zum 
> laufen zu bringen.  Das neue ceval.c brauechte keine 
> nested scopes, keine iterator-bytecodes, keine 
> namespace-optimierungen etc., weil sich diese features 
> in pyeval.py mithilfe der restlichen bytecodes 
> simulieren lassen koennen muessten. 

Nach obigen Überlegungen glaube ich fast, daß das
garkein minimaler ceval.c ist, sondern nur ein
kleiner Support-Interpreter zum effizienten Zugriff
auf Datenstrukturen, auf dem der Bytecode-Emulator
aufsetzt.

> Mir gefaellt an diesem Vorgehen, dass man von python-code 
> losgeht und der C-Code schrittweise vereinfacht bzw. 
> pythonifiziert wird.  Ohne Armin's Magie waeren wir 
> allerdings geschwindigkeits-technisch verloren. 

Das ist wahr. Mal sehen, was er sagt.

ciao - chris

-- 
Christian Tismer             :^)   <mailto:tismer at tismer.com>
Mission Impossible 5oftware  :     Have a break! Take a ride on Python's
Johannes-Niemeyer-Weg 9a     :    *Starship* http://starship.python.net/
14109 Berlin                 :     PGP key -> http://wwwkeys.pgp.net/
work +49 30 89 09 53 34  home +49 30 802 86 56  pager +49 173 24 18 776
PGP 0x57F3BF04       9064 F4E1 D754 C2FF 1619  305B C09C 5A3B 57F3 BF04
      whom do you want to sponsor today?   http://www.stackless.com/






More information about the Python-de mailing list