0

我正在用 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 );

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

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

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

4

3 回答 3

2

将我的评论变成答案,因为这听起来像是您要执行的操作:

如果您正好有 256 个操作码,那么使用操作码索引数组以获取函数的 256 个函数引用(不是函数名称)的数组应该是最快的。如果您在操作码中有任何漏洞(例如,没有使用所有 256 个操作码),您只需为您不使用的操作码填写一个虚拟函数引用。如果您有任何常用函数(提供多个操作码的函数),只需多次使用相同的函数引用即可。这个想法是您想要一个 256 元素数组,您可以使用操作码直接索引该数组以获取要执行的函数。

在某些 JS 引擎中,解释器可以很好地优化仅被视为纯数组的数组,这比对象键查找要好得多。

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

于 2015-02-27T16:34:19.787 回答
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, ...)
于 2015-02-27T10:45:17.103 回答
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);

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

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

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

于 2015-03-12T10:18:44.167 回答