Som v 1.7

New virtual machine and update som v1.5 to add features of som-v2 as follows:

New features

1) new Virtual machine, T-code, 3 times faster than som v1.5
2) use hash in symbol table, (symtab5-s.txt  in som-v2)
3) use snapshot with new object layout (include DS)
4) dynamic load source file   (syscall 11)

and add some new syntax

5) static array:

    a = array {1 2 3}

6) hex number:

    #ff

this is already done in som v1 but not enable (see som1/parser/parse.txt and lex.c).

7) add named file i/o to handle more than one stream.  (see example code in \test\file-s.txt)

presently som-v15 has print, printc, getchar
som-v17 add fopen, fclose, fprint, fprintc, fgetc

syscall number:
1 print
2 printc
3 getchar
4 -
5 fopen
6 fclose
7 fprint
8 fprintc
9 fgetc
10 array
11 load

T-code

T-code specification

Som v1.7 is based on a new virtual machine.  The new instruction set, called T-code, is register-based.  The instruction is 3-address format.  T-code is a register-based VM as opposed to S-code which is stack-based (in the same sense as JVM).  As T-code is 3-address format, it has less number of instruction executed (dynamic instruction count) than S-code.  The data from various chip designs pointed to 40% number of instruction executed of S-code [aisd eecon 2003, sr, compact code jcsse 2005, xs].  This means if all else is equal, T-code vm will be 2.5 times faster than s-code.  The implementation of T-code is carefully engineered and the result (measured running all solutions of 8-queen problem) shows it is 3 times faster than som v1.5.

So, Som v1.7 is going for speed.  However, it will not be complex. For example, the number of instruction is not much larger than s-code, T-code is 54 instructions vs. S-code 44 instructions. The compiler optimisation should not be too complex either (to make future change simple). 

The next release I would like to upgrade Som-v2 to Som-v2.3 to be analogous to v1.7 (but  is all in Som).

23 December 2005

Dynamic load

Finally, in this release (v1.7) the dynamic load is available (after 2 attempts in the past years which ended in failure).  Dynamic load requires the ability to do recursive load.  The most intrigue part is how to handle internal states of "lex" (the lexical analyser).  Lex uses many states to do indentation as blocks (see the source lex.c).  After many designs, the best solution comes from treating a file as a unit of compilation.  It means one whole file will be compiled (generated executable codes) until completion before executing any immediate lines (which have already been compiled into executable codes).  During the execution of the immediate lines, loading of another source file may occurs.  It can happen recursively.  With this design, "lex" is reset to a known initial state for each file because one file will be completed before the next.  It is not necessary to save/restore any states, or any buffer.  Hence, the problem is solved!

eval() may be called recursively.  The global state of the interpreter is fp.  fp is used in the manner of LIFO hence needed not to be saved.  ip is local to eval, so eval() is reentrant.  When doing eval() and encounters "load" (syscall 11), this causes the interpreter to start compiling a new file and executing the immediate lines in that file by calling eval() recursively.

A source file is compiled as a unit, therefore any forward reference to a function must be declared properly.  Remember that this file will be compiled to the completion before starting a next one.  See the following example:

<file t1.txt>
aa = 11
to t1 x = x + 1
to t2 x = {}    // forward declare of func "t2"

load "t2.txt"
print t1 2 print t2 3
<end>

<file t2.txt>
bb = 22
to t2 x = x + 2
<end>

The file "t1.txt" loads "t2.txt".  However, "print t2 3" in t1.txt refers to the function "t2" in other source file not yet compiled.  Therefore the function "t2" must be declare forwardly in the file "t1.txt" before it can be used.  (the compiler must know its arity to compile "print t2 3" properly).  To declare a function forwardly, only the function head must be presented, the body is empty.

Dynamic load enables writing a program as "modules".  A module contains related functions.  Each module is in a separate source file.  A "master" file can be written to contain mainly loading other modules.  The dynamic load helps to manage source files and allows their reuse.

Benchmark

som v1.7  is benchmark against som v1.5.  Table 1 shows the number of instruction executed.  T-machine has less than half the number of instruction executed than S-mcahine.  On average T-machine execute 40% noi. of S-machine.

Table 1  Comparing the number of instruction executed between T-machine and S-machine
program
noi T-code
noi S-code
bubble (20 items) 4207
11925
hanoi (6 disks) 1238
2317
matmul2  (4x4 mul3) 4116
13886
perm (4 digits) 1839
5469
queen2 (all soln) 221578
443344
quick (20 items) 1748
3972
sieve (<= 500) 5250
17151
aes4 11127
28609*

* data from vy2, aes4 (aes2 with static array) by vy2 noi is 28609, vy2 is slightly better than s-machine

In term of running time,  som v1.7 and som v1.5 run "queen2.obj" in excecute only mode 10 times to measure only the effectiveness of interpreter.  The measurement is repeated 3 times.  som v1.7 is 3.4 times faster than som v1.5  (mostly because of engineering of the interpreter, T-code itself contributes around 25% speedup compared with S-code when all things are equal).

Table 2  Comparing the running time of som v1.7 and som v1.5
execution time (run queen2.obj 10 times)
som-v15     4.091, 3.558, 3.914
som-v17     0.248, 0.244, 0.243

11 Jan 2006

<note correction to the speedup figures, 5 Aug 2008.  The figure above is incorrect>
running queen2.obj  100 times, v18 has the same vm as v17. These figures are from birthday/doc/all-som.performance.xls, Aug 2008.
som-v15  1792 ms
som-v18    531 ms
speedup is (1- v18/v15) * 100  =  70.4%   or  3.4x

last update 21 Feb 2024