Javascript &引用;函数调用是昂贵的”;vs.“;“保持功能小型化”;

Javascript &引用;函数调用是昂贵的”;vs.“;“保持功能小型化”;,javascript,performance,Javascript,Performance,一方面,我读到或听说“函数调用很昂贵”,并且它们会影响效率(例如) 然而,另一方面,似乎可以接受的是,函数/方法最好保持简短,并且应该只执行一项任务,正如在中普遍接受的那样 我在这里遗漏了什么,还是这两条建议互相矛盾?有什么经验法则可以让你保持禅宗般的平衡吗 适用于所有语言的一般规则是:使函数(方法、过程)尽可能小。当您添加正确的命名时,您将获得非常可维护和可读的代码,您可以轻松地将重点放在一般情况上,并深入到有趣的细节。用一个巨大的方法,你总是在看细节,大局是隐藏的 这条规则特别适用于聪明的语

一方面,我读到或听说“函数调用很昂贵”,并且它们会影响效率(例如)

然而,另一方面,似乎可以接受的是,函数/方法最好保持简短,并且应该只执行一项任务,正如在中普遍接受的那样


我在这里遗漏了什么,还是这两条建议互相矛盾?有什么经验法则可以让你保持禅宗般的平衡吗

适用于所有语言的一般规则是:使函数(方法、过程)尽可能小。当您添加正确的命名时,您将获得非常可维护和可读的代码,您可以轻松地将重点放在一般情况上,并深入到有趣的细节。用一个巨大的方法,你总是在看细节,大局是隐藏的

这条规则特别适用于聪明的语言和编译器,它们可以进行奇特的优化,比如发现哪些方法不是真正的虚拟方法,因此不需要双重分派


回到JavaScript——这在很大程度上取决于JavaScript引擎。在某些情况下,我希望引擎能够内联函数,避免执行成本,特别是在紧循环中。但是,除非您有性能问题,否则最好使用较小的函数。可读性更为重要。

在一个完美的世界里,没有bug(因为代码只是神奇地自我修复),需求从一开始就被冻结,在这个世界里,可能会有巨大的全能功能

但在这个世界上,它变得太昂贵了——不仅仅是在“人月”方面。尼古拉斯·扎卡斯(Nicholas Zakas)写道,软件开发人员目前面临的大部分挑战

这种转变似乎有些人为,但我的观点是“一个功能一个任务”的方法更易于维护和灵活,换句话说,它最终让开发人员和客户都感到高兴


不过,这并不意味着你不必尽可能少地使用函数调用:只要记住这不是最优先考虑的问题。

我的经验法则是,如果一个函数的长度超过一个满是行的屏幕,那么是时候把它分解成更小的部分了,虽然我的许多函数只是自然地比它小一些,而没有被“人为地”拆分。我通常会留下足够的空白,即使是满屏的屏幕也不是一大堆代码

我尝试让每个函数只执行一个任务,但是一个任务可能是“重新绘制屏幕”,这将涉及在单独函数中实现的一系列子任务,而这些子任务又可能在单独的函数中具有各自的子任务

从可读性(因此易于维护)的自然(对我来说)开始,我不担心函数调用会很昂贵,除非某一段代码在测试时性能很差——然后我会考虑让事情恢复正常(特别是在循环中,从嵌套循环开始)。尽管已经说过,有时您只知道某个特定的代码不会很好地执行,并在进行测试之前重写它

我会避免“过早优化”,尤其是使用智能编译器的语言,这些编译器可能会在幕后进行同样的优化。当我第一次启动C#时,有人告诉我,由于JIT编译器的工作方式,在运行时将代码分解成更小的函数可以降低成本


回到我的单屏幕完整规则,在JavaScript中,嵌套函数是很常见的(这是由于JS闭包的工作方式),这会使包含函数比我使用另一种语言时希望的长,因此有时最终结果是折衷的。

函数调用总是很昂贵(尤其是在for cycles中)内联并不像你想象的那样频繁

Node.js(任何版本)附带的V8引擎都应该广泛地进行内联,但实际上这种功能受到很大限制

下面的代码片段证明了我的观点(Win10x64上的节点4.2.1)

+/-性能下降20%

人们本以为V8 JIT编译器会内联这些函数,但实际上,
a
b
c
可以在代码中的其他地方调用,对于V8的低挂果内联方法来说,它们不是很好的候选方法


我已经看到很多代码(Java、Php、Node.js)在生产过程中由于方法或函数调用的滥用而性能低下:如果您编写Matryoshka风格的代码,运行时性能将随着调用堆栈的大小而线性下降,尽管在概念上看起来很干净。

对所有人来说:这更像是一种“评论”。承认。我选择使用“答案”的空格。请容忍

@斯特凡诺夫拉蒂尼:请把我的笔记作为你工作的基础。我想避免批评别人

这里有两种方法可以进一步改进您文章中的代码:

  • 使用来自process.hrtime()的两部分元组。它返回一个数组[秒,纳秒]。您的代码使用元组的纳秒部分(元素1),我找不到它使用秒部分(元素0)
  • 对单位要明确
我能和我的咆哮相匹配吗?不知道下面是Stephano代码的发展。它有缺陷;如果有人告诉我这件事,我不会感到惊讶。那没关系

"use strict";

var a = function(val) { return val+1; }

var b = function(val) { return val-1; }

var c = function(val) { return val*2 }

var time = process.hrtime();

var reps = 100000000

for(var i = 0; i < reps; i++) { a(b(c(100))); }

time = process.hrtime(time)
let timeWith = time[0] + time[1]/1000000000
console.log(`Elapsed time with function calls: ${ timeWith } seconds`);

time = process.hrtime();
var tmp;
for(var i = 0; i < reps; i++) { tmp = 100*2 - 1 + 1; }

time = process.hrtime(time)
let timeWithout = time[0] + time[1]/1000000000
console.log(`Elapsed time without function calls: ${ timeWithout } seconds`);

let percentWith = 100 * timeWith / timeWithout
console.log(`\nThe time with function calls is ${ percentWith } percent\n` +
    `of time without function calls.`)

console.log(`\nEach repetition with a function call used roughly ` +
        `${ timeWith / reps } seconds.` +
    `\nEach repetition without a function call used roughly ` +
        `${ timeWithout / reps } seconds.`)
像Stephano一样,我使用Win10和Node(我使用v6.2.0)

我承认以下论点:

  • “从透视角度看,光在一纳秒(十亿分之一,1e-9)内传播大约12英寸。”
  • “我们只讨论了很小的纳秒数(47到5),所以谁会关心百分比呢?”
  • “有些算法每秒调用无数函数,因此它们加起来就足够了。”
  • “我们大多数开发人员都不使用这些工具
    Elapsed time function calls: 127.332373
    Elapsed time NO function calls: 104.917725
    
    "use strict";
    
    var a = function(val) { return val+1; }
    
    var b = function(val) { return val-1; }
    
    var c = function(val) { return val*2 }
    
    var time = process.hrtime();
    
    var reps = 100000000
    
    for(var i = 0; i < reps; i++) { a(b(c(100))); }
    
    time = process.hrtime(time)
    let timeWith = time[0] + time[1]/1000000000
    console.log(`Elapsed time with function calls: ${ timeWith } seconds`);
    
    time = process.hrtime();
    var tmp;
    for(var i = 0; i < reps; i++) { tmp = 100*2 - 1 + 1; }
    
    time = process.hrtime(time)
    let timeWithout = time[0] + time[1]/1000000000
    console.log(`Elapsed time without function calls: ${ timeWithout } seconds`);
    
    let percentWith = 100 * timeWith / timeWithout
    console.log(`\nThe time with function calls is ${ percentWith } percent\n` +
        `of time without function calls.`)
    
    console.log(`\nEach repetition with a function call used roughly ` +
            `${ timeWith / reps } seconds.` +
        `\nEach repetition without a function call used roughly ` +
            `${ timeWithout / reps } seconds.`)
    
    Elapsed time with function calls: 4.671479346 seconds
    Elapsed time without function calls: 0.503176535 seconds
    
    The time with function calls is 928.397693664312 percent
    of time without function calls.
    
    Each repetition with a function call used roughly 4.671479346e-8 seconds.
    Each repetition without a function call used roughly 5.0317653500000005e-9 seconds.