javascript - 性能: Switch vs Polymorphism

标签 javascript switch-statement polymorphism v8 vtable

如果可能的话,我通常更喜欢多态而不是切换。我发现它更具可读性并且需要更少的行数。我相信这些事实足以继续使用它。但性能呢?我创建了一个非常简单(而且很糟糕)的工作台,看起来在我的情况下切换速度更快。您能解释一下原因吗?

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/

相关文章:

c - 不中断切换和 i++ 与++i

java - 如何在java中的case语句中输入Double值

class - 如何处理类内部的多态性

c++ - 多新建一删除

javascript - 如何使用 jQuery 获取内部样式表?

javascript - 加载 Facebook 像素异步

c# - 带委托(delegate)或开关的字典?

javascript - 变量变量在 angularjs 中可能吗?

php - 如何通过 jquery $.ajax 从较大的表单内部处理文件上传表单?

php - 在不触及核心的情况下修改 Opencart 中的 Controller ?