Nach einer Weile wurde ich in das von Lennart Benschop geschriebene sod32- Fort-System eingeführt . Dieses Fort-System verfügt über eine in C geschriebene virtuelle Maschine, und das in die virtuelle Maschine geladene Binärbild hängt nicht von der Bytereihenfolge im Worthostsystem ab. sod32 hat 32 Grundelemente, hier sind sie:
NOOP
( --- )
Do nothing
SWAP
( x1 x2 --- x2 x1 )
Swap the two top items on the stack.
ROT
( x1 x2 x3 --- x2 x3 x1 )
Rotate the three top items on the stack.
0=
( x --- f)
f is true if and only if x is 0.
NEGATE
( n1 --- -n1)
Negate top number on the stack.
UM*
( u1 u2 --- ud )
Multiply two unsigned numbers, giving double result.
C@
( c-addr --- c)
Fetch character c at c-addr.
@
( a-addr --- x)
Fetch cell x at a-addr.
+
( w1 w2 --- w3)
Add the top two numbers on the stack.
AND
( x1 x2 --- x3)
Bitwise and of the top two cells on the stack.
OR
( x1 x2 --- x3)
Bitwise or of the top two cells on the stack.
XOR
( x1 x2 --- x3)
Bitwise exclusive or of the top two cells on the stack.
U<
( u1 u2 ---- f)
f is true if and only if unsigned number u1 is less than u2.
<
( n1 n2 --- f)
f is true if and only if signed number n1 is less than n2.
LSHIFT
( x1 u --- x2)
Shift x1 left by u bits, zeros are added to the right.
RSHIFT
( x1 u --- x2)
Shift x1 right by u bits, zeros are added to the left.
UM/MOD
( ud u1 --- urem uquot)
Divide the unsigned double number ud by u1, giving unsigned quotient and remainder.
+CY
( n1 n2 cy1 --- n3 cy2)
Add n1 and n2 and the carry flag cy1 (must be 0 or 1) giving sum n3 and carry flag cy2. (n3 cy2 can be seen as a double number)
SCAN1
( x d --- u)
Scan x for the first 1 bit. u is the position of that bit (counted from the scan direction) and 32 if x=0. d is the scan direction, 0 is left-to-right, 1 is right-to-left.
SPECIAL
( x ---)
Any of a large number of special instructions, indicated by x.
DROP
( x ---)
Discard the top item on the stack.
>R
( x ---)
Push x on the return stack.
C!A
( c c-addr --- c-addr)
Store character c at c-addr, keep address.
!A
( x a-addr --- a-addr)
Store cell x at a-addr, keep address.
DUP
( x --- x x )
Duplicate the top cell on the stack.
OVER
( x1 x2 --- x1 x2 x1)
Copy the second cell of the stack.
R@
( --- x)
x is a copy of the top of the return stack.
R>
( --- x)
Pop the top of the return stack and place it on the stack.
0
( --- 0)
The constant number 0.
1
( --- 1)
The constant number 1.
4
( --- 4)
The constant number 4.
LIT
( --- lit)
Push literal on the stack (literal number is in-line).
Ich erkannte, dass ich die Möglichkeit hatte, die Antwort auf meine Frage zu finden, und begann, Primitive in Definitionen auf hoher Ebene umzuwandeln. Ich möchte sofort darauf hinweisen, dass all diese Aktivitäten eine rein akademische Bedeutung haben. Es ist unwahrscheinlich, dass die aufgrund des Leistungsverlusts erzielten Ergebnisse in der Praxis angewendet werden können. Während ich experimentierte, verfolgte ich die Größe der Fort-System-Binärdatei und die Laufzeit der John Hayes-Testsuite. Ich habe mit dem Befehl ein neues Binärbild erstellt
echo 'S" extend-cross.4" INCLUDED '|./sod32 kernel.img
und führte die Tests wie folgt aus:
time ./sod32 kernel.img <<< $(echo -e 'S" tester.fr" INCLUDED\n12345\nBYE')
In der folgenden Tabelle können Sie sehen, wie sich jede Änderung auf Größe und Leistung auswirkt. Links aus der Spalte "Ändern" führen zum entsprechenden Commit im Github.
| die Veränderung | kernel.img Größe | Ausführungszeit tester.fr |
| original sod32 | 10164 | 0m0.059s |
| lshift, rshift | 10312 | 0m0.071s |
| +, um *, um / mod | 11552 | 0m0.123s |
| c @, c! a | 11952 | 0m0.795s |
| 0 =, negiere <, u < | 12340 | 0m2.800s |
| fallen | 12784 | 0m5.022s |
| tauschen, verrotten, vorbei | 13436 | 0m5.860s |
| sp @, sp !, rp @, rp !, dup | 13680 | 0m8.696s |
| r @, r>,> r | 14160 | 0m15.463s |
| und oder xor | 14336 | 0m21.198s |
| 0, 1, 4 | 15236 | 0m21.671s |
| 0zweig | 15912 | 0m41.765s |
Infolgedessen stieg die Größe des Binärbildes des Fort-Systems von 10164 auf 15912 (+ 57%), die Leistung sank um das 708-fache (fast 3 Größenordnungen). Vielleicht könnte die Leistung durch Profilierung des Codes und Optimierung von Engpässen verbessert werden, aber ich bin sicher, dass das Ergebnis im Vergleich zum ursprünglichen sod32 immer noch sehr langsam sein wird.
Aus 32 Grundelementen plus zusätzlicher Logik im internen Interpreter ergab sich 7: nop, nand,!, @, Um +, special, lit. Der interne Interpreter behält die Logik zum Ausführen von Grundelementen und Definitionen auf hoher Ebene (Aufruf) sowie die Logik zum Vervollständigen der Definitionen auf hoher Ebene (Weiter) bei. Ich fand die Antwort auf meine Frage: Ein Fort-System kann auf der Basis von 9 Grundelementen aufgebaut werden (oder 8, wenn nop kein Grundelement sein muss). Ich bin verwirrt über die Tatsache, dass es bis zu 3 Grundelemente für den Speicherzugriff gibt: @ ,! und beleuchtet, aber ich habe nicht herausgefunden, wie ich es vermeiden kann. Ich hätte etwas übersehen können. Wenn Sie also wissen, wie Sie eine große Anzahl von Grundelementen loswerden können, schreiben Sie bitte in die Kommentare.