通过函数名称查找表调用javascript函数(或方法)的最快方法?

时间:2015-02-27 10:34:28

标签: javascript performance indexed indirection jump-table

我正在用JavaScript模拟一个8位微处理器。我已将每个操作码函数名称存储在一个数组中,并相对于从我的虚拟内存中读取的操作码调用256个函数中的每一个,如下所示:

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

我最近修改了我的代码以摆脱eval(),如下所示:

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

在Mac Safari中,上述任何一种都没有明显的速度差异,这让我感到惊讶。我认为后者会更快但我的虚拟时钟速度大致相同(目前达到峰值4MHz)。

由于与使用eval()相比,使用索引方法调用似乎没有速度惩罚,我希望也更新我的虚拟内存系统,但我对使用的语法有一个心理障碍。

要写一个字节,我有:

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 );

我正在使用这种索引的间接方法,因此我可以将设备映射到地址范围,并根据需要放置断点和观察点,这些断点和观察点都会拦截内存读写 - 从而最大限度地提高性能。

有关如何在保持数据吞吐量的同时丢失评估的任何建议?

我想将外部方法映射到间接数组中,并且能够传递参数(要写入的值)。因此,无论虚拟硬件是使用公共接口访问虚拟内存,虚拟硬件的其他位都可以拦截进程并在必要时监视或更改值。

3 个答案:

答案 0 :(得分:2)

将我的评论转化为答案,因为它听起来像你要做的那样:

如果你有256个操作码,那么使用操作码索引到数组中获取函数的256个函数引用(不是函数名)的数组应该是最快的。如果操作码中有任何漏洞(例如,不使用全部256个操作码),则只需为未使用的操作填写虚拟函数参考。如果您有任何常用功能(一个提供多个操作码的功能),则只需多次使用相同的功能参考。我们的想法是你想拥有一个256元素的数组,你可以用一个操作码直接索引它,以获得一个执行的函数。

在某些JS引擎中,解释器可以非常好地优化仅作为纯数组处理的数组,远远超过对象键查找。

与所有与性能相关的问题一样,您必须在相关浏览器中进行测试才能确定。使用eval()不太可能是最快的,因为它必须首先从头开始解析代码然后执行它。

答案 1 :(得分:0)

你试过了吗?

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

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

或者看看这个: stackoverflow.com/questions/1986896/what-is-the-difference-between-call-and-apply

theFunction.apply(valueForThis, arrayOfArgs)

theFunction.call(valueForThis, arg1, arg2, ...)

答案 2 :(得分:0)

使用问题中详述的eval方法的替代方法:

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);

这比使用初始eval方法(在Mac Safari中)快约5%。它也更整洁,并删除了使用eval。 for ...循环中函数定义的复杂性是由于setr函数的本地创建的变量addr的范围。如果直接使用,则指向此变量的指针将传递给内部函数,当循环完成时,所有数组元素将具有相同的“地址”,即循环结束时的addr值(start + length)。因此,我通过外部函数在循环的每次迭代中创建一个新的局部变量(a),它是一个常量,并且在每个内部函数中使用该值(通过指针)。

另请注意,我使用对象名称(RAM)而不是.this来访问内存数组,因为其他对象可能间接访问内部方法并导致.this引用调用对象。直接命名对象可以解决这个问题。

有关在循环中创建函数的更详细示例和说明,请参阅callbacks in loops