Skip to content

Instantly share code, notes, and snippets.

@Nafees10
Created October 24, 2019 21:24
Show Gist options
  • Save Nafees10/d438de2ac56f93bf455453204fc525c1 to your computer and use it in GitHub Desktop.
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
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;
}
@Nafees10
Copy link
Author

Nafees10 commented Oct 24, 2019

Weird thing is:
Running the noOutput program, Switcher is faster
Running the showOutput program, FCall is faster

so in conclusion, I still cant decide

EDIT:
After further testing, it's clear the switch case method is faster.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment