簡體   English   中英

“函數調用很昂貴”與“保持函數小”

[英]“Function calls are expensive” vs. “Keep functions small”

一方面,我讀到或聽到“函數調用很昂貴”並且它們會影響效率(例如, 在Nicholas Zakas的Google技術談話中 )。

在另一方面,然而,似乎接受了函數/方法最好剪短,只應該真正執行一個任務,如一般在接受這里

我在這里遺漏了什么,或者這兩條建議是否相互沖突? 是否有一些經驗法則允許人們保持禪宗般的平衡?

適用於所有語言的一般規則是:保持函數(方法,程序)盡可能小。 當您添加適當的命名時,您將獲得非常易於維護和可讀的代碼,您可以輕松地專注於一般情況並深入了解有趣的細節。 通過一種巨大的方法,您始終可以查看詳細信息並隱藏大圖。

此規則特別適用於聰明的語言和編譯器,它們可以執行花哨的優化,例如內聯或發現哪些方法不是真正的虛擬,因此不需要雙重調度。

回到JavaScript - 這在很大程度上依賴於JavaScript引擎。 在某些情況下,我會期望合適的引擎內聯函數,避免執行成本,特別是在緊密循環中。 但是,除非遇到性能問題,否則請選擇較小的功能。 可讀性更重要。

在一個完美的世界中,沒有錯誤(因為代碼只是神奇地修復了自己),並且要求從第一天開始就被凍結,因此可能有巨大的無所不能的功能。

但在這個世界上,它變得過於昂貴 - 而且不僅僅是“人月”。 Nicholas Zakas寫了一篇精彩的文章,描述了軟件開發人員目前面臨的大多數挑戰。

過渡可能看起來有點人為,但我的觀點是“一個功能 - 一個任務”的方法更加可維護和靈活 - 換句話說,它最終使得兩個開發人員和客戶滿意。

但是,這並不意味着您不會盡可能少地使用函數調用:只要記住它不是最重要的。

我的經驗法則是,如果它不僅僅是一個充滿線條的屏幕,那么它是時候將一個函數分解成更小的部分,盡管我的許多函數自然最終比沒有“人工”分割的情況稍微小一些。 而且我通常會留下足夠的空白區域,即使是滿屏也不是很多代碼。

我嘗試讓每個函數只執行一個任務,但是一個任務可能是“重新繪制屏幕”,這將涉及在單獨的函數中實現的一系列子任務,而這些子任務又可能在不同的函數中具有自己的子任務。

開始時感覺自然(對我來說)的可讀性(因此易於維護)我不擔心函數調用是昂貴的,除非特定的代碼片段在測試時表現不佳 - 然后我會考慮把事情帶回來-line(特別是在循環中,從嵌套循環開始)。 雖然說過,有時你只是知道某段特定代碼不能很好地執行並在進行測試之前重寫它...

我會避免“過早優化”,特別是使用可能在幕后進行相同優化的智能編譯器的語言。 當我第一次啟動C#時,我被告知,由於JIT編譯器的工作方式,在運行時將代碼分解為較小的函數可能會便宜。

回到我的一個屏幕完整規則,在JavaScript中,通常有嵌套函數(由於JS閉包的工作方式),如果我使用其他語言,這可以使包含函數比我想要的更長,所以有時最終的結果是妥協。

函數調用總是很昂貴(特別是在循環中)並且內聯不會像你想象的那樣經常發生

與Node.js(任何版本)一起提供的V8引擎應該進行廣泛的內聯,但實際上這種能力受到很大限制。

以下(微不足道的)代碼片段證明了我的觀點(Win10x64上的節點4.2.1)

"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();

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

console.log("Elapsed time function calls: %j",process.hrtime(time)[1]/1e6);

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

console.log("Elapsed time NO function calls: %j",process.hrtime(time)[1]/1e6);

結果

Elapsed time function calls: 127.332373
Elapsed time NO function calls: 104.917725

+/- 20%的性能下降

人們原本期望V8 JIT編譯器可以內聯這些函數,但實際上abc可以在代碼中的其他地方調用,並且不適合用V8獲得的低掛水果內聯方法

我看到很多代碼(Java,Php,Node.js)由於方法或函數調用濫用而在生產中表現不佳:如果你編寫代碼Matryoshka樣式,運行時性能會隨着調用堆棧大小而線性降低 ,盡管看起來概念上干凈。

對所有人來說:這更像是一種“評論”的感覺。 確認。 我選擇使用“答案”的空間。 請容忍。

@StefanoFratini:請把我的筆記作為建立在你的工作。 我想避免批評。

以下是進一步改進帖子中代碼的兩種方法:

  • 使用來自process.hrtime()的元組的兩半。 它返回一個數組[seconds,nanoseconds]。 你的代碼使用元組的納秒部分(元素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代碼的后代。 結果完全不同。

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.

像Stephano一樣,我使用了Win10和Node(對我來說是v6.2.0)。

我承認這些論點

  • “從視角來看,在納秒級(十億分之一,1e-9)中,光線傳播大約12英寸。”
  • “我們只討論少量的納秒(47到5),那么誰在乎百分比呢?”
  • “有些算法每秒都會進行數以萬計的函數調用,因此可以為它們增加數量。”
  • “我們大多數開發人員都不使用這些算法,因此擔心函數調用的數量對我們大多數人來說都會適得其反。”

我會對經濟論點撒謊:我的計算機和前面的計算機每個都花費不到400美元(美國)。 如果一個軟件工程師每小時收入大約90到130美元,那么他們老板的時間價值就像我的一台計算機到三到四個小時的工作。 在那種環境中:

這與公司在需要的軟件停止工作時損失的每小時美元相比如何?

當付費客戶暫時無法使用業務合作伙伴生產的收縮包裝軟件時,這與失去的善意和聲望相比如何?

還有很多其他問題。 我會省略它們。

當我解釋答案時:可讀性和可維護性統治着計算機性能。 我的建議? 相應地編寫代碼的第一個版本。 我尊重的很多人說短功能有幫助。

完成代碼並且不喜歡性能后,找到阻塞點。 我尊重的許多人都表示,這些觀點從來都不是你所期望的。 當你認識他們時工作。

所以雙方都是對的。 一些。

我? 我猜我離開了某個地方。 兩分錢。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM