Created
October 24, 2019 21:24
-
-
Save Nafees10/d438de2ac56f93bf455453204fc525c1 to your computer and use it in GitHub Desktop.
2 janky "VM"s to test if using switch-case is better than function calls to call instructions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import std.stdio; | |
| import std.datetime.stopwatch; | |
| void main(string[] args){ | |
| FCall caller; | |
| Switcher swExec; | |
| StopWatch sw; | |
| if (args.hasElement("output")){ | |
| caller = new FCall(cast(ubyte[])outputProgramInstructions, cast(int[])outputProgramArgs, 64); | |
| swExec = new Switcher(cast(ubyte[])outputProgramInstructions, cast(int[])outputProgramArgs, 64); | |
| }else{ | |
| caller = new FCall(cast(ubyte[])programInstructions, cast(int[])programArgs, 64); | |
| swExec = new Switcher(cast(ubyte[])programInstructions, cast(int[])programArgs, 64); | |
| } | |
| caller.push(1); | |
| swExec.push(1); | |
| if (args.hasElement("sw")){ | |
| writeln("starting Switcher"); | |
| sw.start(); | |
| swExec.execute; | |
| sw.stop; | |
| }else{ | |
| writeln("starting FCall"); | |
| sw.start(); | |
| caller.execute; | |
| sw.stop; | |
| } | |
| writeln("execution took: ", sw.peek.total!"msecs", "msecs"); | |
| } | |
| /// Instructions: | |
| /// 0 : push | |
| /// 1 : add | |
| /// 2 : isLess | |
| /// 3 : jumpIf (to zero) | |
| /// 4 : Writeln | |
| /// 5 : push popped element twice | |
| class Switcher{ | |
| private: | |
| ubyte[] _program; | |
| int[] _args; | |
| Stack _stack; | |
| public: | |
| this(ubyte[] program, int[] args, uint maxStack){ | |
| _program = program.dup; | |
| _args = args.dup; | |
| _stack = new Stack(maxStack); | |
| } | |
| ~this(){ | |
| .destroy(_stack); | |
| } | |
| /// pushes to stack | |
| void push(int element){ | |
| _stack.push(element); | |
| } | |
| /// Executes program | |
| void execute(){ | |
| int* _argPtr = &_args[0]; | |
| ubyte* instPtr = &_program[0]; | |
| ubyte* lastInstruction = &_program[_program.length-1] + 1; | |
| while (instPtr < lastInstruction){ | |
| switch (*instPtr){ | |
| case 0: | |
| _stack.push(*_argPtr); | |
| break; | |
| case 1: | |
| _stack.push(_stack.pop + _stack.pop); | |
| break; | |
| case 2: | |
| _stack.push(cast(int)(_stack.pop > _stack.pop)); | |
| break; | |
| case 3: | |
| if (_stack.pop == 1){ | |
| instPtr = &_program[0] - 1; | |
| _argPtr = &_args[0] - 1; | |
| } | |
| break; | |
| case 4: | |
| writeln(_stack.pop); | |
| break; | |
| case 5: | |
| int element = _stack.pop; | |
| _stack.push([element, element]); | |
| default: | |
| break; | |
| } | |
| instPtr++; | |
| _argPtr++; | |
| } | |
| } | |
| } | |
| class FCall{ | |
| private: | |
| void delegate(int)[] _program; | |
| int[] _args; | |
| Stack _stack; | |
| void delegate(int)* _instPtr; | |
| int* _argPtr; | |
| void delegate(int)[ubyte] _instructionTable; | |
| void pushRuntime(int a){ | |
| _stack.push(a); | |
| } | |
| void add(int a){ | |
| _stack.push(_stack.pop + _stack.pop); | |
| } | |
| void isLess(int a){ | |
| _stack.push(cast(int)(_stack.pop > _stack.pop)); | |
| } | |
| void jumpIf(int a){ | |
| if (_stack.pop == 1){ | |
| _instPtr = &_program[0] - 1; | |
| _argPtr = &_args[0] - 1; | |
| } | |
| } | |
| void writeLn(int a){ | |
| writeln(_stack.pop); | |
| } | |
| void rePush(int a){ | |
| int element = _stack.pop; | |
| _stack.push([element, element]); | |
| } | |
| public: | |
| this(ubyte[] program, int[] args, uint maxStack){ | |
| _args = args.dup; | |
| _stack = new Stack(maxStack); | |
| _instructionTable[0] = &this.pushRuntime; | |
| _instructionTable[1] = &this.add; | |
| _instructionTable[2] = &this.isLess; | |
| _instructionTable[3] = &this.jumpIf; | |
| _instructionTable[4] = &this.writeLn; | |
| _instructionTable[5] = &this.rePush; | |
| // translate the program | |
| _program.length = program.length; | |
| foreach (i, inst; program){ | |
| _program[i] = _instructionTable[inst]; | |
| } | |
| } | |
| ~this(){ | |
| .destroy(_stack); | |
| } | |
| /// pushes to stack | |
| void push(int element){ | |
| _stack.push(element); | |
| } | |
| /// executes the program | |
| void execute(){ | |
| _instPtr = &_program[0]; | |
| _argPtr = &_args[0]; | |
| void delegate(int)* lastInstruction = &_program[_program.length - 1] + 1; | |
| while (_instPtr < lastInstruction){ | |
| (*_instPtr)(*_argPtr); | |
| _instPtr ++; | |
| _argPtr ++; | |
| } | |
| } | |
| } | |
| /// Fixed max-length stack (not using utils.lists.Stack because that one isnt optimized to be fast as much as this should be) | |
| /// | |
| /// Be aware that no bound checking is done here, so be careful | |
| package class Stack{ | |
| private: | |
| /// the actual stack | |
| int[] _stackArray; | |
| /// pointer of the next element that'll be written to next | |
| int* _peekPtr; | |
| public: | |
| this(uint length=64){ | |
| _stackArray.length = length; | |
| _peekPtr = _stackArray.ptr; | |
| } | |
| /// Reads n number of elements from stack, in reverse order (i.e: [nth-last pushed, (n-1)th-last pushed, ..., last pushed]) | |
| /// | |
| /// Returns: the elements read | |
| int[] pop(uint n){ | |
| _peekPtr -= n; | |
| return _peekPtr[0 .. n]; | |
| } | |
| /// Reads the last element pushed to stack | |
| /// | |
| /// Returns: the element pop-ed | |
| int pop(){ | |
| _peekPtr --; | |
| return *_peekPtr; | |
| } | |
| /// pushes elements to stack. First in array is pushed first | |
| void push(int[] elements){ | |
| _peekPtr[0 .. elements.length] = elements; | |
| _peekPtr += elements.length; | |
| } | |
| /// pushes an element to stack | |
| void push(int element){ | |
| *_peekPtr = element; | |
| _peekPtr ++; | |
| } | |
| /// Returns: number of elements present | |
| @property uint count(){ | |
| return cast(uint)(_peekPtr - _stackArray.ptr); | |
| } | |
| /// Returns: the element at an index (this is possible as the stack is actually an array) | |
| int read(uint index){ | |
| return _stackArray[index]; | |
| } | |
| /// Writes a value to an index on the stackArray (possible because the stack is actually a stack) | |
| void write(uint index, int value){ | |
| _stackArray[index] = value; | |
| } | |
| } | |
| /// program, with output | |
| const ubyte[] outputProgramInstructions = [ | |
| 0, | |
| 1, | |
| 5, | |
| 4, | |
| 5, | |
| 0, | |
| 2, | |
| 3 | |
| ]; | |
| /// program, with output | |
| const int[] outputProgramArgs = [ | |
| 1, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 1000000 | |
| ]; | |
| /// program, without output | |
| const ubyte[] programInstructions = [ | |
| 0, | |
| 1, | |
| 5, | |
| 0, | |
| 2, | |
| 3 | |
| ]; | |
| /// program, without output | |
| const int[] programArgs = [ | |
| 1, | |
| 0, | |
| 0, | |
| 1000000 | |
| ]; | |
| /// Returns: true if an aray has an element, false if no | |
| /// | |
| /// I stole it from my repo github/nafees10/utils | |
| bool hasElement(T)(T[] array, T element){ | |
| bool r = false; | |
| foreach(cur; array){ | |
| if (cur == element){ | |
| r = true; | |
| break; | |
| } | |
| } | |
| return r; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Weird thing is:
Running the noOutput program,
Switcheris fasterRunning the showOutput program,
FCallis fasterso in conclusion, I still cant decide
EDIT:
After further testing, it's clear the switch case method is faster.