【问题标题】:How to Disable V8's Optimizing Compiler如何禁用 V8 的优化编译器
【发布时间】:2013-08-30 20:20:58
【问题描述】:

我正在编写一个常量时间字符串比较函数(用于 node.js),并且想禁用 V8 的优化编译器来实现这个单一的函数;使用命令行标志是不可能的。

我知道使用with{}(或try/catch)块会禁用优化编译器现在,但恐怕这个“功能”(错误)将在未来的版本中得到修复.

是否有一种不可变(并记录在案)的方式来禁用 V8 的优化编译器?


示例函数:

function constantTimeStringCompare( a, b ) {
    // By adding a `with` block here, we disable v8's optimizing compiler.
    // Using Object.create(null) ensures we don't have any object prototype properties getting in our way.our way.
    with ( Object.create( null ) ){
        var valid = true,
            length = Math.max( a.length, b.length );
        while ( length-- ) {
            valid &= a.charCodeAt( length ) === b.charCodeAt( length );
        }
        // returns true if valid == 1, false if valid == 0
        return !!valid;
    }
}

还有一个 perf test 只是为了好玩。

【问题讨论】:

  • @DavidMurdoch 这称为线性时间,因为作为输入长度函数的执行时间是linear function。此外,这个函数可能比内置字符串比较 worse - 你缺少 三个 非常常见的快捷方式。
  • @DavidMurdoch 您的第一个问题是一个非常人为的场景。通常,运行时函数的“值”是在给定大小为n 的输入的情况下执行给定操作的次数。在您的情况下,此操作将是字符比较。因为这将是 longer 字符串的长度,所以 a 是常量并不重要,因为对于所有字符串 ba 长,此函数的值将是 @ 987654330@。 (由于有无限数量的字符串长于a,而短于a 的字符串数量有限,我们可以在分析中忽略较短的字符串。)
  • @DavidMurdoch 另外,对于算法复杂度,您通常会考虑渐近复杂度。此处可以忽略检查引用相等性的快捷方式。我现在记得的另一个是检查长度是否相等,这是一个恒定时间检查,假设字符串长度事先已知。 (就像 Javascript 中的情况一样。)这意味着使用它的函数对于不等长度的输入将是恒定时间,对于等长输入是线性时间。所以是的,在这种情况下,省略这些检查将保证您的函数对于所有字符串都是 linear-time
  • @DavidMurdoch 而使用这些快捷方式,理论上对于大多数字符串对来说是 constant-time,对于所有字符串对的无穷小子集来说是线性的。实际上,这取决于您的输入范围。即使没有它们,正如您所注意到的那样,由于.lengthmax() 的开销,函数也不会是完全线性的
  • @DavidMurdoch 是的。 It's also incorrect. 您的函数确定a 是否是b 的前缀。 (为了使这成为一个恒定时间的比较,实际上您必须实现我提到的“优化”。)将所有操作计入计数器也没有任何意义。您通常分别计算一个或有时几个“代表性”操作的实例。对输入元素(在您的情况下为字符)的读取或写入很常见,输入元素之间的比较也是如此。您可以放心地忽略持续开销。

标签: javascript node.js google-chrome v8 compiler-optimization


【解决方案1】:

如果您想要可靠的方法,您需要使用--allow-natives-syntax 标志运行节点并调用它:

%NeverOptimizeFunction(constantTimeStringCompare);

请注意,您应该在调用 constantTimeStringCompare 之前调用它,如果函数已经优化,那么这违反了断言。

否则with 声明是您最好的选择,因为使其可优化将是绝对的疯狂,而支持try/catch 将是合理的。不过,您不需要它来影响您的代码,这就足够了:

function constantTimeStringCompare( a, b ) {
    with({});

    var valid = true,
        length = Math.max( a.length, b.length );
    while ( length-- ) {
        valid &= a.charCodeAt( length ) === b.charCodeAt( length );
    }
    // returns true if valid == 1, false if valid == 0
    return !!valid;

}

仅仅提及with 语句会破坏整个包含函数 - 优化是在函数级别的粒度上完成的,而不是每个语句。

【讨论】:

  • 不幸的是,使用标志不是一个选项。我觉得 V8 的优化编译器可能有一天会优化空的 with 块。您不认为这种优化是一种潜力吗?如果没有,为什么?
  • @DavidMurdoch 没有进行诸如删除死代码之类的优化,因为该函数通过提及with 禁用了优化。在这种优化发生之前,他们必须在他们的优化编译器中开始支持with,我已经说过这首先是疯了,因为哲学是支持合理的代码。 with 在严格模式下是纯粹的邪恶甚至语法错误,不是人们会急于优化的合理代码。但如果你不想使用标志,这是你最好的选择。
  • 8 年后:一位同事问我为什么调试器看不到函数中的某些变量,而技术上它们应该是。我告诉他我打赌是优化编译器将它们从范围中删除,因为函数没有使用它们。我提到很久以前有人告诉我,在函数中使用 with({}){} 会导致优化器退出——今天仍然如此(至少在附加了调试器的 Node v14.18.2 中)。 :-)
【解决方案2】:

要实际检查特定 Node.js 版本是否优化了功能,您可以参考 bluebird 的 Optimization Killers wiki。
我在 Node 7.2 上检查了 3 个解决方案:

  1. with({}) - TurboFan 优化功能
  2. try {} catch(e) {} - TurboFan 优化功能
  3. eval(''); - 功能未优化

所以为了保证禁用 V8 优化,你应该在函数体中添加 eval('')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-14
    • 2011-08-11
    • 2010-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多