【问题标题】:How to keep debug code from costing you runtime performance in Javascript如何防止调试代码在 Javascript 中降低运行时性能
【发布时间】:2020-05-14 07:35:35
【问题描述】:

我有一些时间敏感的异步代码,只能通过记录实时数据然后查看日志来进行调试。因此,我使用如下语句对代码进行了检测:

DBG(`Setting ${name} timer for ${amount} ms from runMore(${reason})`);

DBG() 的实现如下:

let debugOn = process.env["DEBUG_RATE_LIMIT_MAP"] === "1";
let DBG;
if (debugOn) {
    DBG = function(...args) {
        args.unshift(time() + ": ");
        console.log(...args);
    }
} else {
    DBG = function() {};
}

一般的想法是,如果设置了环境变量,它只会输出到控制台。

这一切都很好,但令我困扰的是,即使在未设置环境变量时它不会输出到控制台,它仍然会评估所有调试语句的参数,例如:

DBG(`Setting ${name} timer for ${amount} ms from runMore(${reason})`);

因此,它仍在为每个语句做丢弃工作(评估模板字符串)。显然,Javascript 不是像 C++ 这样的预处理语言,您可以在其中“编译”调试代码。 Javascript 中有哪些技术可以让您在代码库中保留调试代码(以供将来需要进行调试时使用),但不会在不使用调试时降低运行时性能?

对于任何其他代码参考,整个模块在这里:https://github.com/jfriend00/async-utils/blob/master/rateMap.js。

【问题讨论】:

  • 如果您使用任何类型的构建链,JS 与任何预处理语言一样好 - 它只是编译为(不同的)JS。有转译器插件可以从代码中删除调试语句。
  • @Bergi - 当前未使用构建链。此外,不要真的希望删除代码,因为那样您必须获得调试版本才能进行调试。所以,也许我的 C++ 类比不好,因为那不是我真正想要的。我希望有一些实时成本更低的运行时方案。
  • 嗯,好的。为此,我唯一能想到的就是使用标记的模板文字,这样您就可以避免字符串连接。 (虽然不确定它的开销,你需要进行基准测试)。
  • @Bergi - 我从来没有真正理解标记文字的常见用途。也许这是一种可能性,尽管在调用标记函数之前仍然会解析模板文字,但它还没有产生完整的输出。

标签: javascript debugging preprocessor


【解决方案1】:

您是否需要 DBG 始终成为function?如果没有,你可以这样做:

let debugOn = process.env["DEBUG_RATE_LIMIT_MAP"] === "1";
let DBG;
if (debugOn) {
    DBG = function(...args) {
        args.unshift(time() + ": ");
        console.log(...args);
    }
}

然后在你的代码中,而不是直接调用DBG,你检查它是否是一个函数:

if (typeof DBG === "function") {
    DBG(`Setting ${name} timer for ${amount} ms from runMore(${reason})`);
}

这样,您确实有if 条件检查的开销,但可以避免字符串文字在生产环境中执行。

【讨论】:

  • 是的,这可以防止额外的模板评估,但是在你的代码中撒上所有东西是一种痛苦。我希望有更清洁的东西。而且,在这个具体的例子中,我可以只使用if (debugOn)...。
  • 如果您使用打字稿,您可以使用DBG?.(stringLiteral),这样会更干净。最新的 ES 也会有它:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 如果未定义 DBG,DBG?.(templateLiteral) 是否会避免评估 templateLiteral?这听起来可能很有趣。
  • 应该的。正如文档中提到的那样,它会在DBG 本身短路。相当于DBG && DBG(templateLiteral)
  • 这很好,但if 条件在可读性方面更好,除非您将返回值分配给某个变量。 codereadability.com/…
【解决方案2】:

基于一些 cmets,我目前最好的想法是这样的:

// environment variable that turns debug tracing on
let debugOn = process.env["DEBUG_RATE_MAP"] === "1";

function DBG(...args) {
    if (debugOn) {
        args.unshift(time() + ": ");
        console.log(...args);
    }
}

然后,在代码中这样做:

if (debugOn) DBG(`Setting ${name} timer for ${amount} ms from runMore(${reason})`);

而且,如果它只是一个普通的字符串文字(所以不需要避免模板评估),我可以这样做:

DBG('All done.');

【讨论】:

  • 另一件事:将其设为const debugOn = …。这样,优化编译器可以推断出 false 值不会改变,并且可以完全删除 if 块。
  • @Bergi - 我有意识地选择使用let,因此如果需要,我可以在调试时以编程方式更改值。不过,了解优化是件好事。
猜你喜欢
  • 2020-06-02
  • 2022-12-15
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
  • 2019-01-30
  • 1970-01-01
  • 1970-01-01
  • 2011-10-12
相关资源
最近更新 更多