我正在用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 );
我正在使用这种索引的间接方法,因此我可以将设备映射到地址范围,并根据需要放置断点和观察点,这些断点和观察点都会拦截内存读写 - 从而最大限度地提高性能。
有关如何在保持数据吞吐量的同时丢失评估的任何建议?
我想将外部方法映射到间接数组中,并且能够传递参数(要写入的值)。因此,无论虚拟硬件是使用公共接口访问虚拟内存,虚拟硬件的其他位都可以拦截进程并在必要时监视或更改值。
答案 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。