0

I am simulating an 8-bit microprocessor with JavaScript. I have stored each opcode function name in an array, and call each of the 256 functions relative to the opcode read from my virtual memory, as follows:

this.OP[opcode] = 'this.LDAA()';
eval(this.OP[opcode]);

I have recently altered my code to get rid of the eval() as below:

this.OP[opcode] = 'LDAA';
this[this.OP[opcode]]();

In Mac Safari there is no discernible speed difference between either of the above, which surprised me. I thought the latter would be faster but my virtual clock speed is about the same for both (presently peaking at 4MHz).

As there appears to be no speed penalty by using indexed method calls compared with using eval() I wish to also update my virtual memory system, but I am having a mental block about the syntax to use.

To write a byte I have:

RAM = {
    write : [],
    setup : function() {
        this.write[addr] = "this.simpleWrite(addr,byte)";
    },

    writeByte : function(addr,byte) { 
        eval(this.write[addr]);
    },

    simpleWrite : function(addr,byte) { 
        this.memory[addr] = byte;
    },
};

RAM.writeByte( someAddress, someValue );

I am using this indexed, indirect method so I can map devices into the address range and place breakpoints and watchpoints as required which all intercept memory reads and writes - maximising performance.

Any suggestions on how to lose the eval's whilst maintaining data throughput?

I want to map external methods into the indirection array, and be able to pass parameters (values to write). So that whatever piece of virtual hardware is accessing the virtual memory using a common interface, other bits of virtual hardware can intercept the process and monitor or alter values if necessary.

Luke Peterson
  • 7,732
  • 8
  • 41
  • 45
  • 2
    If you have exactly 256 opcodes, then an array of 256 function references (not function names) where you use the opcode to index into the array to fetch the function should be the fastest. Arrays that are treated only as pure arrays can be optimized pretty nicely by the interpreter in some JS engines, much more so than object key lookups. As with all performance-related questions, you will have to test in relevant browsers to be sure. Using `eval()` is unlikely to ever be fastest. – jfriend00 Feb 27 '15 at 10:39
  • What is the value of `addr` in your `setup`??? – Bergi Feb 27 '15 at 11:27
  • addr is any valid index of the write[] array (actually 0 to 65535). I will look up the difference between function names and references, thats a good idea if I am correct in what you mean thanks. – rjcostello Feb 27 '15 at 11:35
  • @jfriend00 - I don't have 256 functions as some are duplicated, NOPs etc but I can easily expand it to 256 then have an array of anonymous functions called by index - I like the sound of that, thank you. – rjcostello Feb 27 '15 at 12:01

3 Answers3

2

Turning my comment into an answer since it sounds like what you're going to do:

If you have exactly 256 opcodes, then an array of 256 function references (not function names) where you use the opcode to index into the array to fetch the function should be the fastest. If you have any holes in the opcodes (e.g. not using all 256 opcodes), you just fill in a dummy function reference for the ones you aren't using. If you have any common functions (a function that serves more than one opcode), just use the same function reference more than once. The idea is that you want to have a 256 element array that you can directly index into with an opcode to get a function to execute.

Arrays that are treated only as pure arrays can be optimized pretty nicely by the interpreter in some JS engines, much more so than object key lookups.

As with all performance-related questions, you will have to test in relevant browsers to be sure. Using eval() is unlikely to ever be fastest because it has to first parse the code from scratch and then execute it.

jfriend00
  • 580,699
  • 78
  • 809
  • 825
  • Having tried it using evals, methods with names stored in an array and as you suggest, with the functions as array elements indexed directly there is only 5% variance in Mac Safari. On average the functions as array elements are running at 3.7MHz, and function names as array elements at 3.5MHz. But the function names method does peak the highest at 4.2MHz (compared with 4.0). I got a few extra MHz by removing all comments. So the answer is that Safari's compiler is pretty efficient no matter which method is used. – rjcostello Feb 27 '15 at 23:53
  • This is so obviously the right answer every other reply seems downright insane. I'll add that `Function.prototype.bind` is pretty handy for this too -- just pass a reference to a function if you have no parameters to bind, and bind some basic params if you can re-use a function. Here's an emulator with an array used as an opcode map: https://github.com/jkoudys/remu/blob/4d55e07eaffa590ff158096b89088e032ca8b643/js/utils/emulator/z80.js#L1380 – Josh from Qaribou Aug 31 '15 at 16:35
0

did you try :

setup : function() {
    this.write[addr] = "simpleWrite";
},

writeByte : function(addr,byte) { 
    this[this.write[addr]](addr,byte);
},

Or give a look at this : stackoverflow.com/questions/1986896/what-is-the-difference-between-call-and-apply

theFunction.apply(valueForThis, arrayOfArgs)

theFunction.call(valueForThis, arg1, arg2, ...)
Community
  • 1
  • 1
Federico
  • 1,236
  • 9
  • 12
  • That's starting me off on the right track, I'll give it a go later thank you. – rjcostello Feb 27 '15 at 12:02
  • The problem there is `writeByte()` can't indirect through external methods. I think instead of `this.write[]` being an array of function names, it needs to be an array of functions. Then mapping a new device will consist of replacing the indexed function definition within the array. It will take time to implement but I will try - thank you for making me think more clearly. – rjcostello Feb 27 '15 at 12:14
0

The alternative to using the eval approach detailed in the question:

RAM = {
    memory : [],
    write : [],
    setup : function() {
        for (var addr = start; addr < length; ++addr) {
            this.write[addr] = 
                function(a) { 
                    function(byte) { 
                        RAM.memory[a] = byte 
                    }
                }(addr);
            }
        }
    }
RAM.setup();
RAM.write[validAddress](byteValue);

This is about 5% faster than using the initial eval approach (in Mac Safari). It is also tidier and removes the use of eval. The complexity of the function definition within the for...loop is due to the scope of the variable addr which is created local to the setup function. A pointer to this variable would be passed to the inner function if used directly and when the loop completes all array elements would then have the same 'address' being the value of addr at the end of the loop (start+length). So, I create a new local variable (a), which is a constant, on each iteration of the loop via an outer function and this value is used (via a pointer) within each inner function.

Also note that I access the memory array using the object name (RAM) rather than .this because other objects may access the inner method indirectly and cause .this to reference the calling object. Naming the object directly solves this issue.

For a more detailed example and explanation about creating functions within loops see callbacks in loops.