偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

我的 JavaScript 比你的 Rust 更快

開發(fā)
JavaScript 的可移植性確實(shí)更好,而且特別適合云原生開發(fā)場(chǎng)景;但如果大家特別看重性能,那 Rust 可能是個(gè)更好的選擇。

作者Josh Urbane 是一位從業(yè)多年的軟件架構(gòu)師,很喜歡在社交媒體分享技術(shù)觀點(diǎn)。近日,他寫了一篇文章,記錄了自己憑借經(jīng)驗(yàn)贏了與新人開發(fā)者打賭的故事,而“我的 JavaScript 比你的 Rust 更快”的結(jié)論也是來(lái)自這個(gè)打賭。他的故事或許可以說(shuō)明運(yùn)行策略在研發(fā)實(shí)踐中的重要性。

對(duì)我來(lái)說(shuō),軟件架構(gòu)師這活兒最讓人開心的一點(diǎn)就是能指導(dǎo)開發(fā)者理解最新的概念、影響他們的技術(shù)判斷。有些開發(fā)者不是很囂張嗎,那就用理論加現(xiàn)實(shí)啪啪打他們的臉;架構(gòu)師還得負(fù)責(zé)營(yíng)造出寓教于樂(lè)的學(xué)習(xí)氛圍,幫助年輕氣盛的開發(fā)者逐漸長(zhǎng)大成熟。

最會(huì)讓我在心里暗爽的事兒就是一個(gè)愣頭青開發(fā)者突然跳出來(lái)、想要挑戰(zhàn)我的技術(shù)建議(從開發(fā)者的視角看,架構(gòu)師就是一幫總在提「錯(cuò)誤」建議的傻瓜),而且賭上全部身家堅(jiān)持認(rèn)為自己的辦法更好。

問(wèn)題是,我已經(jīng)干這行很久了,不用驗(yàn)證我就知道問(wèn)題的正確答案是什么。所以那就來(lái)唄,咱們手底下見(jiàn)真章,我把這段故事記錄了下來(lái)、在幾年后整理成了今天的這篇文章。

梭哈是一種“智慧”

老實(shí)講,下面要講的這個(gè)事已經(jīng)過(guò)去好幾年了,所以很多細(xì)節(jié)我已經(jīng)記不清楚。大體情況就是結(jié)合當(dāng)時(shí)團(tuán)隊(duì)的知識(shí)儲(chǔ)備、可用工具庫(kù)和原有技術(shù)債務(wù),我給出的建議是讓大家使用 Node.js。

一個(gè)新任初級(jí)開發(fā)者對(duì)自己剛拿到的計(jì)算機(jī)科學(xué)學(xué)士證書很有信心,想要用“炫技”的方式挫挫我的銳氣。他們聽(tīng)說(shuō)我是輔修的計(jì)算機(jī)科學(xué),所以覺(jué)得我壓根不了解計(jì)算機(jī)底層原理。其實(shí)剛畢業(yè)那會(huì)我也認(rèn)為自己很懂,但這行干久了,我越來(lái)越覺(jué)得計(jì)算機(jī)系統(tǒng)像是魔法……

他的信心并非毫無(wú)來(lái)由,這個(gè)結(jié)論如同“C++ 比 JavaScript 速度快”,基本屬于業(yè)界共識(shí)。但作為典型的架構(gòu)師,我仍然堅(jiān)持認(rèn)為“要視情況而定”。

更具體地講,“經(jīng)過(guò)充分優(yōu)化的 C++,確實(shí)比具有同等優(yōu)化水平的 JavaScript 跑得更快”,畢竟 JavaScript 有著無(wú)法避免的執(zhí)行開銷(即便如此,我們也可以把代碼編譯成靜態(tài)程序來(lái)獲得高度接近 C++ 的性能)。反正話已至此,那就梭了唄。

意外的是,JavaScript 代碼確實(shí)要比 C++ 版本更快一點(diǎn),而且從架構(gòu)設(shè)計(jì)的角度來(lái)看,JS 版本可以由當(dāng)前團(tuán)隊(duì)一力維護(hù)、不需要借助其他部門的技術(shù)能力。

還好還好,其實(shí)我也不敢百分之百確定自己是對(duì)的,但考慮到這個(gè)用例中的內(nèi)存對(duì)象大小可能是動(dòng)態(tài)的、再加上那位年輕開發(fā)者確實(shí)經(jīng)驗(yàn)不足,所以我愿意賭上一把。

JS 比 C++ 還快,怎么實(shí)現(xiàn)的?

我猜大多數(shù)開發(fā)者都理解不了這樣的結(jié)果。這明顯跟“編譯”語(yǔ)言快于“解釋”語(yǔ)言、“靜態(tài)”程序快于“VM”程序的基本原則背道而馳啊。但請(qǐng)注意,這些只是經(jīng)驗(yàn)、而非真理。

我之前也提到,“優(yōu)化”才是決定速度的關(guān)鍵。畢竟即使 C++ 語(yǔ)言自身的性能優(yōu)勢(shì)再?gòu)?qiáng),糟糕的編寫質(zhì)量也會(huì)讓程序身陷泥潭。另一方面,Node.js(使用基于 C++/C 的 V8 與 libuv 庫(kù))則更具優(yōu)化空間,所以實(shí)際運(yùn)行速度并不差。甚至可以說(shuō),質(zhì)量同樣差勁的 JS 和 C++ 程序,JS 的性能可能還更好一點(diǎn)。但這只是宏觀論述,下面咱們來(lái)看點(diǎn)細(xì)節(jié)。

內(nèi)存是關(guān)鍵

大多數(shù)開發(fā)者應(yīng)該很熟悉棧和堆的概念,但這種理解基本只停留在了表面——例如只知道棧是線性的,而堆就是帶有指針的“坨”(并非嚴(yán)格術(shù)語(yǔ),大家能理解就行)。

更重要的是,棧和堆的概念對(duì)應(yīng)著多種實(shí)現(xiàn)和方法。底層硬件并不知道“堆”是個(gè)什么東西,因?yàn)閮?nèi)存的管理方式是由軟件來(lái)定義的,而內(nèi)存管理方面的選擇必然會(huì)對(duì)程序的最終性能產(chǎn)生巨大影響。

大家也可以就這個(gè)問(wèn)題深挖下去,很有意義也很有價(jià)值。現(xiàn)代硬件和內(nèi)核都相當(dāng)復(fù)雜,其中往往包含大量具有特殊用途的優(yōu)化機(jī)制,例如更高效地利用高級(jí)內(nèi)存布局。這意味著軟件可以(或者必須)借用由硬件提供的內(nèi)存管理功能。此外還有虛擬化的影響……這里就不多做展開了。

魔法的核心:垃圾回收

沒(méi)錯(cuò),Node.js 解決方案的啟動(dòng)時(shí)間肯定更長(zhǎng),因?yàn)樗枰ㄟ^(guò) JIT 編譯器來(lái)實(shí)現(xiàn)腳本的加載和運(yùn)行。不過(guò)一旦加載完成,Node.js 代碼其實(shí)反而擁有一項(xiàng)神秘的優(yōu)勢(shì)——垃圾回收機(jī)制。

而在 C++ 程序中,應(yīng)用程序往往會(huì)在堆中創(chuàng)建動(dòng)態(tài)大小的對(duì)象,之后再將其刪除。這意味著程序的分配器必須一遍又一遍地在堆中分配和釋放內(nèi)存。這項(xiàng)操作本身速度較慢,而且實(shí)際性能基本由分配器中的算法決定。在多數(shù)情況下,dealloc 的速度會(huì)特別慢,即使是精簡(jiǎn)后的 alloc 也沒(méi)好太多。

對(duì)于 Node.js 程序,這項(xiàng)絕技就是程序只運(yùn)行一次就會(huì)退出。Node.js 同樣運(yùn)行腳本并分配必要的內(nèi)存,但后面的刪除操作會(huì)由垃圾回收器挑選空閑時(shí)間再推遲執(zhí)行。

誠(chéng)然,垃圾回收機(jī)制在本質(zhì)上并不比其他內(nèi)存管理策略更好或者更差(一切都是權(quán)衡),但在我們打賭的這個(gè)特定程序中,垃圾回收確實(shí)能顯著提升性能,因?yàn)檫@個(gè)程序壓根就沒(méi)真正運(yùn)行過(guò)。我們只是把一大堆對(duì)象塞進(jìn)內(nèi)存,再在退出時(shí)一次性丟棄。

垃圾回收肯定是有代價(jià)的,Node.js 進(jìn)程占用的內(nèi)存容量明顯大于 C++ 程序。這就是“省 cpu= 費(fèi)內(nèi)存”和“省內(nèi)存 = 費(fèi) cpu”的經(jīng)典難題,但我的目標(biāo)就是打那小子的臉,所以費(fèi)點(diǎn)內(nèi)存也無(wú)所謂。

而我之所以能贏,是因?yàn)閷?duì)方選擇了一個(gè)幼稚的策略。其實(shí)他要想贏,最好的辦法就是添加內(nèi)存泄漏,故意把所有分配都保留在內(nèi)存當(dāng)中。這樣 C++ 程序的內(nèi)存占用量還是更小,但速度卻比原先快得多?;蛘?,他也可以用給棧分配緩沖區(qū)之類的設(shè)計(jì)來(lái)進(jìn)一步提高性能,這種辦法在實(shí)際生產(chǎn)中其實(shí)經(jīng)常用到。

另外還有如何選擇性能基準(zhǔn)的問(wèn)題。一般來(lái)說(shuō),大家比較的就是每秒操作數(shù)量。這里的 JS 對(duì) C++ 就是個(gè)很好的例子,證明了“先理解總體性能成本,再做選擇”往往更加靠譜。在軟件架構(gòu)中,我們必須得時(shí)刻關(guān)注資源層面的“總體擁有成本”。

步入現(xiàn)代:有請(qǐng) Rust 上場(chǎng)

Rust 是我目前最喜歡的語(yǔ)言之一。它提供了很多現(xiàn)代特性、速度很快,而且具備良好的內(nèi)存模型,生成的代碼也相當(dāng)安全。

Rust 當(dāng)然不是完美的,它的編譯時(shí)間比較長(zhǎng)、涉及不少奇奇怪怪的語(yǔ)義,但總體來(lái)說(shuō)還是值得推薦。大家可以對(duì) Rust 中的內(nèi)存管理方式進(jìn)行靈活控制,但其“?!眱?nèi)存始終遵循所有者模型(ownership model),這也是其實(shí)現(xiàn)引以為傲的高安全性能的基礎(chǔ)。

我目前參與的一個(gè)項(xiàng)目就是用 Rust 編寫的 FaaS(函數(shù)即服務(wù))主機(jī),負(fù)責(zé)執(zhí)行 WASM(WebAssembly)函數(shù)。它能快速安全地執(zhí)行各項(xiàng)隔離函數(shù),最大限度降低 FaaS 的運(yùn)行開銷。它的速度也很快,每核心每秒能夠處理 90000 個(gè)簡(jiǎn)單請(qǐng)求。更重要的是,它的總內(nèi)存占用量只有 20 MB 上下,可以說(shuō)相當(dāng)夸張了。

但這跟 Node.js 與 C++ 的賭局有什么關(guān)系?

簡(jiǎn)單來(lái)說(shuō),我是把 Node.js 視為“合理”的性能基準(zhǔn)(Go 屬于「夢(mèng)幻」級(jí)基準(zhǔn),它的性能絕對(duì)不是那些專為 Web 服務(wù)設(shè)計(jì)的語(yǔ)言能比肩的,這里就別降維打擊了),畢竟我們那款程序的早期 C++ 版本性能實(shí)在不咋的,唯一的好處就是內(nèi)存占用量只有 Node.js 版本的不到十分之一。

雖然先讓代碼跑起來(lái)、再對(duì)代碼做優(yōu)化確實(shí)沒(méi)啥毛病,但在 C++ 這種“快”語(yǔ)言上輸給了 JavaScript 肯定讓人非常沮喪。而我之所以敢當(dāng)場(chǎng)梭哈,靠的就是對(duì)明顯瓶頸的基本判斷。這個(gè)瓶頸就是內(nèi)存管理。

每個(gè) guest 函數(shù)都被分配到一個(gè)內(nèi)存數(shù)組,但在函數(shù)之內(nèi)分配內(nèi)存,以及在函數(shù)內(nèi)存與主機(jī)內(nèi)存間復(fù)制數(shù)據(jù)肯定會(huì)帶來(lái)大量性能開銷。由于動(dòng)態(tài)數(shù)據(jù)被四處亂扔,分配器相當(dāng)于是飽受四面八方的重拳打擊。至于解決辦法嘛,作弊嘍!

加堆,兩個(gè)堆、三個(gè)堆......

從本質(zhì)上講,堆代表的是分配器用來(lái)管理映射的一部分內(nèi)存。程序會(huì)請(qǐng)求 N 個(gè)內(nèi)存單元,分配器在可用的內(nèi)存池里搜尋這些單元(或者向主機(jī)請(qǐng)求更多內(nèi)存)及存儲(chǔ)哪些單元已被占用,之后再返回該內(nèi)存的位置指針。當(dāng)程序用盡內(nèi)存時(shí),就會(huì)告知分配器,再由分配器更新映射以明確現(xiàn)在哪些單元已經(jīng)再次可用。挺簡(jiǎn)單的,對(duì)吧?

但如果我們需要分配一大堆生命周期有別、大小各異的內(nèi)存單元時(shí),麻煩就來(lái)了。這一定會(huì)產(chǎn)生大量碎片,進(jìn)而放大了新內(nèi)存的分配成本。于是性能損失開始產(chǎn)生,畢竟分配器的功能太過(guò)簡(jiǎn)單,只是在尋找可用的存儲(chǔ)位置。

這個(gè)問(wèn)題顯然沒(méi)有太好的解決方案,雖然目前可選的分配算法很多,但它們還是各有權(quán)衡、要求我們結(jié)合用例特點(diǎn)選擇最適方法(也可以像大多數(shù)開發(fā)者一樣,直接用默認(rèn)選項(xiàng))。

再來(lái)說(shuō)作弊。作弊的辦法可不只一種:對(duì)于 FaaS,我們可以釋放每次運(yùn)行的 dealloc,并在每次運(yùn)行完成后清除整個(gè)堆;我們也可以在函數(shù)生命周期的不同階段使用不同的分配器,例如明確區(qū)分初始化階段和運(yùn)行階段。這樣無(wú)論是干凈的函數(shù)(每次運(yùn)行,都會(huì)被重置為相同的初始內(nèi)存狀態(tài))還是有狀態(tài)函數(shù)(在每次運(yùn)行之間保留狀態(tài)),都能獲得與之對(duì)應(yīng)且經(jīng)過(guò)優(yōu)化的內(nèi)存策略。

在我們的 FaaS 項(xiàng)目里,大家最終構(gòu)建了一個(gè)動(dòng)態(tài)分配器,它會(huì)根據(jù)使用情況選擇分配算法、且實(shí)際選擇會(huì)在每次運(yùn)行之間持續(xù)留存。

對(duì)于“使用率較低”的函數(shù)(也就是大多數(shù)函數(shù)),只使用簡(jiǎn)單的棧分配器用指針指向下一個(gè)空閑槽即可。當(dāng)調(diào)用 dealloc 時(shí),如果該單元為棧上的最后一個(gè)單元,則回滾指針;如果不是最后一個(gè)單元,則無(wú)操作。當(dāng)函數(shù)完成時(shí),指針將被設(shè)置為 0(相當(dāng)于 Node.js 在垃圾回收前退出)。如果函數(shù)的 dealloc 失敗數(shù)和用量達(dá)到一定閾值,則在其余調(diào)用中使用其他分配算法。結(jié)果就是,這套方案在大多數(shù)情況下都能顯著加快內(nèi)存分配。

運(yùn)行時(shí)中還用到了另一個(gè)“堆”——主機(jī)(或者說(shuō)是函數(shù)共享內(nèi)存)。它使用同樣的動(dòng)態(tài)分配策略,并允許繞過(guò)早期 C++ 版本中的復(fù)制步驟、直接寫入函數(shù)內(nèi)存。如此一來(lái),I/O 就能直接從內(nèi)核中復(fù)制 guest 函數(shù),并繞過(guò)主機(jī)運(yùn)行時(shí),從而顯著提高吞吐量。

Node.js 對(duì)陣 Rust

經(jīng)過(guò)優(yōu)化,Rust FaaS 運(yùn)行時(shí)最終比我們的 Node.js 參考實(shí)現(xiàn)快了 70% 以上,而內(nèi)存占用量更是不到后者的十分之一。

但這里的關(guān)鍵在于“經(jīng)過(guò)優(yōu)化”,它的初始實(shí)現(xiàn)其實(shí)速度反而更慢。我們的優(yōu)化還要求對(duì) WASM 函數(shù)做出一些限制,具體限制在編譯過(guò)程中完全公開透明,而且極少出現(xiàn)不兼容的情況。

Rust 版本的最大優(yōu)勢(shì)就是內(nèi)存占用小,省下來(lái)的 RAM 可以用作緩存或者分布式內(nèi)存存儲(chǔ)等其他用途。這意味著 I/O 開銷進(jìn)一步降低,生產(chǎn)運(yùn)行的效率更高,其效果甚至比拉高 CPU 配置還更明顯些。

后續(xù)我們還有更多優(yōu)化計(jì)劃,但主要是為了解決主機(jī)層中一些具有重大安全影響的問(wèn)題。雖然跟內(nèi)存管理或者性能沒(méi)啥關(guān)系,但畢竟也算支持了 “Rust 比 Node 更快”黨們的觀點(diǎn)。

總結(jié)

其實(shí)全文寫下來(lái),我也得不出特別明確的結(jié)論。下面只給出幾個(gè)粗淺的觀點(diǎn):

  • 內(nèi)存管理很有趣,每種方法都是在做取舍。只要策略運(yùn)用得當(dāng),任何一種語(yǔ)言都能獲得巨大的性能提升。
  • 我仍然推薦大家根據(jù)實(shí)際目標(biāo)靈活使用 Node.js 和 Rust,所以這里不做優(yōu)劣判斷。JavaScript 的可移植性確實(shí)更好,而且特別適合云原生開發(fā)場(chǎng)景;但如果大家特別看重性能,那 Rust 可能是個(gè)更好的選擇。
  • 從頭到尾我都在說(shuō) JavaScript,但這里實(shí)際指的是 TypeScript。

歸根結(jié)底,大家得根據(jù)實(shí)際情況選擇最適合的技術(shù)方案。我們?cè)绞橇私獠煌瑮5牟煌卣?,在選擇的時(shí)候就越是從容有數(shù)。

原文鏈接:https://medium.com/@jbyj/my-javascript-is-faster-than-your-rust-5f98fe5db1bf

責(zé)任編輯:趙寧寧 來(lái)源: 前端之巔
相關(guān)推薦

2022-02-16 10:13:29

數(shù)據(jù)中心芯片連接器

2024-11-08 09:19:28

2024-09-27 09:53:22

Rust標(biāo)準(zhǔn)庫(kù)優(yōu)化

2012-03-21 17:03:36

智慧的地球IBM

2012-04-11 11:21:58

IBM專家集成系統(tǒng)智慧的地球

2018-05-11 09:39:35

2015-10-09 08:48:11

javascript思維技巧

2015-10-09 09:33:50

JavaScript思維技巧

2021-02-16 11:04:26

RustGo華為

2022-06-22 10:04:29

JavaScriptRust語(yǔ)言

2020-05-19 07:59:39

JavaScript語(yǔ)言開發(fā)

2022-01-09 23:06:39

JavaScript

2021-12-27 06:57:40

Maven工具性能

2021-07-17 15:48:13

Rust編程語(yǔ)言關(guān)鍵字

2012-03-12 09:33:04

JavaScript

2025-04-14 10:35:00

for 循環(huán)

2021-02-16 16:44:40

RustJavaScript開發(fā)

2024-01-18 13:36:00

RustGo開發(fā)

2019-04-04 13:33:17

2010-05-06 09:23:45

云計(jì)算
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)