如果可能的话,我通常更喜欢多态而不是切换。我发现它更具可读性并且需要更少的行数。我相信这些事实足以继续使用它。但性能呢?我创建了一个非常简单(而且很糟糕)的工作台,看起来在我的情况下切换速度更快。您能解释一下原因吗?
https://jsfiddle.net/oqzpfqcg/1/
var class1 = { GetImportantValue: () => 1 };
var class2 = { GetImportantValue: () => 2 };
var class3 = { GetImportantValue: () => 3 };
var class4 = { GetImportantValue: () => 4 };
var class5 = { GetImportantValue: () => 5 };
getImportantValueSwitch = (myClassEnum) => {
switch (myClassEnum.type) {
case 'MyClass1': return 1;
case 'MyClass2': return 2;
case 'MyClass3': return 3;
case 'MyClass4': return 4;
case 'MyClass5': return 5;
}
}
getImportantValuePolymorphism = (myClass) => myClass.GetImportantValue();
test = () => {
var INTERATION_COUNT = 10000000;
var t0 = performance.now();
for (var i = 0; i < INTERATION_COUNT; i++) {
getImportantValuePolymorphism(class1);
getImportantValuePolymorphism(class2);
getImportantValuePolymorphism(class3);
getImportantValuePolymorphism(class4);
getImportantValuePolymorphism(class5);
}
var t1 = performance.now();
var t2 = performance.now();
for (var i = 0; i < INTERATION_COUNT; i++) {
getImportantValueSwitch({type: 'MyClass1'});
getImportantValueSwitch({type: 'MyClass2'});
getImportantValueSwitch({type: 'MyClass3'});
getImportantValueSwitch({type: 'MyClass4'});
getImportantValueSwitch({type: 'MyClass5'});
}
var t3 = performance.now();
var first = t1 - t0;
var second = t3 - t2;
console.log("The first sample took " + first + " ms");
console.log("The second sample took " + second + " ms");
console.log("first / second = " + (first/second));
};
test();
据我了解,第一个示例有一个动态/虚拟运行时调用 myClass.GetImportantValue()
,仅此而已。但第二个也有一个动态/虚拟运行时调用 myClassEnum.type
,然后检查开关中的条件。
很可能我的代码中有一些错误,但我找不到它。我认为唯一可以影响结果的是performance.now()
。但我认为影响不大。
最佳答案
V8 开发人员在这里。您的直觉是正确的:这个微基准测试并不是很有用。
一个问题是所有“类”都具有相同的形状,因此“多态”情况实际上是单态的。 (如果您修复此问题,请注意 V8 对于 <= 4 和 >= 5 多态情况具有截然不同的性能特征!)
一个问题是,您依赖堆栈替换 (OSR) 进行优化,因此它对性能的影响会以一种误导性的方式污染您的计时 - 特别是对于具有两个连续长时间运行模式的函数循环:它们对第一个循环进行 OSR 优化,在中间进行去优化,然后对第二个循环再次进行 OSR 优化。
一个问题是编译器会内联许多内容,因此实际执行的机器代码可能与您编写的 JavaScript 代码具有非常不同的结构。特别是在这种情况下,getImportantValueSwitch
被内联,{type: 'MyClass*'}
常量对象创建被省略,生成的代码只是一些比较,其中非常快。
一个问题是,对于小函数,调用开销几乎主导了其他一切。 V8 的优化编译器当前不执行多态内联(因为这并不总是成功),因此需要花费大量时间来调用 () => 1
等函数。这与它们是动态分派(dispatch)的事实无关——从对象中检索正确的函数非常快,调用它才是有开销的。对于较大的函数,您不会太注意到它,但对于几乎为空的函数,与不执行任何调用的基于 switch
的替代方案相比,它非常重要.
长话短说:在微基准测试中,人们倾向于测量与想要测量的内容无关的奇怪效果;在较大的应用程序中,大多数像这样的实现细节不会产生可衡量的影响。编写对您有意义的代码(可读、可维护等),让 JavaScript 引擎担心其余的事情! (异常(exception):有时分析表明您的应用程序存在特定瓶颈 - 在这种情况下,手动优化可能会产生很大影响,但这通常是通过考虑上下文并使整体算法/控制流更高效来实现的,而不是遵循简单的经验法则,例如“更喜欢多态性而不是 switch 语句”(或相反)。)
关于javascript - 性能: Switch vs Polymorphism,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50401725/