---- Testing Procedure ---- Five different machine types were studied: 1. novm: An x86 CPU machine. That is, I let the processor execute the code directly instead of through a virtual machine. It is included solely for reference purposes to determine how well each of the different machines fare. 2. quakevm: A three-argument machine as implemented by Quake. All instructions work on registers, and all constants are preloaded by the execution environment into registers. The set of available registers is fixed, so recursive functions are impossible, and each function must be aware of all the others so that one function does not stomp on the registers used by another function. 3. 3arg: An extended three-argument machine that supports different types of registers (constants, local variables, instance variables, default variables, and global variables). In such a machine, local variables would be the primary registers. This would make recursion possible, and functions could be created without worrying about stomping on the register space of other functions. 4. direct: An Unreal-like machine where every instruction is implemented as a native function and functions get their parameters by executing more functions and using their results. I haven't seen this type of machine described anywhere, so I don't know what its official name (if any) is, hence the designation direct, in reference to the ability for code to directly execute itself without the use of an interpreter loop. 5. stack: A stack machine. Operands are pushed onto a stack, and operations manipulate the stack. All machines were tested running the code below (a number after a $ designates a variable): repeat 15 times: $0 = (3*5+8/2)-1 $1 = $0*5*7+3 $2 = (5+7*3/1)/1 $3 = ($0+$1)*($0+$2) $4 = $3/$0+$1+$2 The novm loop was compiled with optimizations turned off so that it would execute the code above exactly. All other code was optimized for speed. ---- Results ---- machine performance novm 1 quakevm 8 3arg 1 11 (only supports constant and local variable registers) direct 1 17 (native functions declared as __fastcall) direct 2 18 (native functions declared as __cdecl) 3arg 2 18 (supports all register types described above) stack 24 Performance is how many times longer code takes to execute than novm. Hence, novm is 1 and quakevm is 8 because it takes 8 times longer to execute than novm. ---- Analysis ---- For raw speed, the quakevm is the best choice. Unfortunately, this machine is so lacking in features that it is unsuitable for my purposes. The remaining machines (aside from the first variant of 3arg) provide a framework I consider to be suitably feature rich. The stack machine was the slowest and can be ruled out for that reason. This was surprising to me. I had expected it to be faster than the direct machine because it doesn't have all the function call overhead, but apparently the overhead associated with maintaining a stack is higher. Of the remaining two machines, direct is slightly faster than 3arg 2 when using __fastcall but not enough to choose it over 3arg specifically for that reason. Fortunately, there are other considerations to help decide which machine type to use. The direct machine is very simple. No distinctions are made between instructions and native functions because they are the same thing. Executing scripted code in native code is as simple as calling a function in a lookup table. Code for this machine is also very close to the parser output a compiler would typically use to create byte code from a source file. It is also fairly easy to read disassembled code for this machine, which has a structure similar to lisp. This machine can also support practically any number of input data sources without penalty, since all inputs are obtained by executing bytecodes/functions. The 3arg machine, in comparison, is more similar to a traditional processor architecture than the direct machine. It can only handle a fixed number of data sources. Data sources that cannot be conceptualized as a set of registers require some extra work. Generating code for this machine is only slightly more involved than generating code for the direct machine, but optimizing it can be more work because a straightforward generation of code from the parser output will tend to be less optimized than a straightforward code generator for the direct machine. Calling native code from scripted code is also more complicated than the direct machine. ---- Conclusion ---- The Unreal-style direct machine is the fastest machine with the desired features (barely). It is also the easiest to write an interpreter and a compiler for.