我在用Javascript编写Gameboy Emulator时发现了一个非常具体的问题。我刚刚完成了音频部分的实现,并使用一个简单的计数器来检查何时需要更新音频逻辑。其背后的原因是因为音频时钟以仿真CPU时钟的一小部分运行。
在模拟器中,我有4个不同的音频通道。所有这些都被编写为具有某种继承形式的ES6类。每个通道负责跟踪计数器,因此我们拥有可用的时钟并在音频通道本身内执行逻辑。但是,当我运行Chrome探查器时,我注意到奇怪的行为表明,只是增加此数字,性能就会受到严重影响:
我尝试了不同的事情:
- 确保我在课程开始时实例化了属性
- 将值放入一个临时变量中,将其递增并放回去(最后一行会导致相同的问题)
var a = this.fs;
a++;
this.fs = a; //This line would cause the same issue.
- 彼此紧接着进行一堆递增和递减操作,以确保这不是一次性的。此变量的每次更改都会对性能造成很大的影响。
//Every line would give a big performance spike causing the emulator to be unplayable.
this.fs++;
this.fs--;
this.fs++;
this.fs--;
this.fs++;
this.fs--;
this.fs++;
this.fs--;
this.fs++;
this.fs--;
this.fs++;
this.fs--;
this.fs++;
因此,我尝试过的所有事情似乎都指向这样一个事实,即更改属性的值会导致性能严重下降。我已经阅读了一些有关V8引擎进行内联缓存的信息,但是到目前为止,还没有发现任何有用的信息。
如果有人想查看完整的源代码,则可以在这里找到AudioChannel代码:
http://www.joranderaaff.nl/gameboyso/src/audiochannels.js
如果您想运行模拟器并尝试看看自己,可以在此处进行播放:
http://www.joranderaaff.nl/gameboyso/
特别是在两个口袋妖怪战斗的介绍动画中,口袋妖怪似乎有些麻烦。
另一个非常奇怪的发现是将计数器包装在一个对象中会大大减少处理时间:
//In the constructor:
this.fs = {counter: 0};
//In the step() method:
this.fs.counter++;
编辑:通过将所需的代码复制到每个类中,我刚刚删除了所有基于继承的代码。这样,不再需要调用“ super”。这大大提高了性能。似乎让类彼此继承可以大大提高V8引擎的性能。