在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

不同語言運行100萬個并發任務需要多少內存?

算法與數據結構 ? 來源:博客園 ? 2023-07-12 14:14 ? 次閱讀

來自:博客園,譯者:InCerry

正文

在這篇博客文章中,我深入探討了異步和多線程編程在內存消耗方面的比較,跨足了如Rust、Go、JavaC#Python、Node.js 和 Elixir等流行語言。

不久前,我不得不對幾個計算機程序進行性能比較,這些程序旨在處理大量的網絡連接。我發現那些程序在內存消耗方面有巨大的差異,甚至超過20倍。有些程序在10000個連接中僅消耗了略高于100MB的內存,但另一些程序卻達到了接近3GB。

不幸的是,這些程序相當復雜,功能也不盡相同,因此很難直接進行比較并得出有意義的結論,因為這不是一個典型的蘋果到蘋果的比較。這促使我想出了創建一個綜合性基準測試的想法。

基準測試

我使用各種編程語言創建了以下程序:

啟動N個并發任務,每個任務等待10秒鐘,然后在所有任務完成后程序就退出。任務的數量由命令行參數控制。

在ChatGPT的小小幫助下,我可以在幾分鐘內用各種編程語言編寫出這樣的程序,甚至包括那些我不是每天都在用的編程語言。為了方便起見,所有基準測試代碼都可以在我的GitHub上找到。

Rust

我用Rust編寫了3個程序。第一個程序使用了傳統的線程。以下是它的核心部分:

letmuthandles=Vec::new();
for_in0..num_threads{
lethandle=thread::spawn(||{
thread::from_secs(10));
});
handles.push(handle);
}
forhandleinhandles{
handle.join().unwrap();
}

另外兩個版本使用了async,一個使用tokio,另一個使用async-std。以下是使用tokio的版本的核心部分:

letmuttasks=Vec::new();
for_in0..num_tasks{
tasks.push(task::spawn(async{
time::from_secs(10)).await;
}));
}
fortaskintasks{
task.await.unwrap();
}

async-std版本與此非常相似,因此我在這里就不再引用了。

Go

在Go語言中,goroutine是實現并發的基本構建塊。我們不需要分開等待它們,而是使用WaitGroup來代替:

varwgsync.WaitGroup
fori:=0;i

Java

Java傳統上使用線程,但JDK 21提供了虛擬線程的預覽,這是一個類似于goroutine的概念。因此,我創建了兩個版本的基準測試。我也很好奇Java線程與Rust線程的比較。

Listthreads=newArrayList<>();
for(inti=0;i{
try{
Thread.sleep(Duration.ofSeconds(10));
}catch(InterruptedExceptione){
}
});
thread.start();
threads.add(thread);
}
for(Threadthread:threads){
thread.join();
}

下面是使用虛擬線程的版本。注意看它是多么的相似!幾乎一模一樣!

Listthreads=newArrayList<>();
for(inti=0;i{
try{
Thread.sleep(Duration.ofSeconds(10));
}catch(InterruptedExceptione){
}
});
threads.add(thread);
}
for(Threadthread:threads){
thread.join();
}

C#

與Rust類似,C#對async/await也有一流的支持:

Listtasks=newList();
for(inti=0;i
{
awaitTask.Delay(TimeSpan.FromSeconds(10));
});
tasks.Add(task);
}
awaitTask.WhenAll(tasks);

Node.JS

下面是 Node.JS:

constdelay=util.promisify(setTimeout);
consttasks=[];

for(leti=0;i

Python

還有Python 3.5版本中加入了async/await,所以可以這樣寫:

asyncdefperform_task():
awaitasyncio.sleep(10)


tasks=[]

fortask_idinrange(num_tasks):
task=asyncio.create_task(perform_task())
tasks.append(task)

awaitasyncio.gather(*tasks)

Elixir

Elixir 也因其異步功能而聞名:

tasks=
for_<-?1..num_tasks?do
????????Task.async(fn?->
:timer.sleep(10000)
end)
end

Task.await_many(tasks,:infinity)

測試環境

硬件: Intel(R) Xeon(R) CPU E3-1505M v6 @ 3.00GHz

操作系統: Ubuntu 22.04 LTS, Linux p5520 5.15.0-72-generic

Rust: 1.69

Go: 1.18.1

Java: OpenJDK “21-ea” build 21-ea+22-1890

.NET: 6.0.116

Node.JS: v12.22.9

Python: 3.10.6

Elixir: Erlang/OTP 24 erts-12.2.1, Elixir 1.12.2

所有程序在可用的情況下都使用發布模式(release mode)進行運行。其他選項保持為默認設置。

結果

最小內存占用

讓我們從一些小的任務開始。因為某些運行時需要為自己分配一些內存,所以我們首先只啟動一個任務。

adaae568-2062-11ee-962d-dac502259ad0.png

圖1:啟動一個任務所需的峰值內存

我們可以看到,這些程序確實分為兩組。Go和Rust程序,靜態編譯為本地可執行文件,需要很少的內存。其他在托管平臺上運行或通過解釋器消耗更多內存的程序,盡管在這種情況下Python表現得相當好。這兩組之間的內存消耗差距大約有一個數量級。

讓我感到驚訝的是,.NET某種程度上具有最差的內存占用,但我猜這可以通過某些設置進行調整。如果有任何技巧,請在評論中告訴我。在調試模式和發布模式之間,我沒有看到太大的區別。

10k 任務

add0b8c4-2062-11ee-962d-dac502259ad0.png

圖2:啟動10,000個任務所需的峰值內存

這里有一些意外發現!大家可能都預計線程將成為這個基準測試的大輸家。這對于Java線程確實如此,實際上它們消耗了將近250MB的內存。但是從Rust中使用的原生Linux線程似乎足夠輕量級,在10000個線程時,內存消耗仍然低于許多其他運行時的空閑內存消耗。異步任務或虛擬(綠色)線程可能比原生線程更輕,但我們在只有10000個任務時看不到這種優勢。我們需要更多的任務。

另一個意外之處是Go。Goroutines應該非常輕量,但實際上,它們消耗的內存超過了Rust線程所需的50%。坦率地說,我本以為Go的優勢會更大。因此,我認為在10000個并發任務中,線程仍然是相當有競爭力的替代方案。Linux內核在這方面肯定做得很好。

Go也失去了它在上一個基準測試中相對于Rust異步所占據的微小優勢,現在它比最好的Rust程序消耗的內存多出6倍以上。它還被Python超越。

最后一個意外之處是,在10000個任務時,.NET的內存消耗并沒有從空閑內存使用中顯著增加??赡芩皇鞘褂昧祟A分配的內存。或者它的空閑內存使用如此高,10000個任務太少以至于不重要。

100k 任務

我無法在我的系統上啟動100,000個線程,所以線程基準測試必須被排除??赡苓@可以通過某種方式調整系統設置來實現,但嘗試了一個小時后,我放棄了。所以在100,000個任務時,你可能不想使用線程。

ade99c18-2062-11ee-962d-dac502259ad0.png

在這一點上,Go程序不僅被Rust擊敗,還被Java、C#和Node.JS擊敗。

而Linux .NET可能有作弊,因為它的內存使用仍然沒有增加。 我不得不仔細檢查一下是否確實啟動了正確數量的任務,果然,它確實做到了。而且它在大約10秒后仍然可以退出,所以它沒有阻塞主循環。神奇!.NET干得好。

100萬任務

現在我們來試試極限場景。

在100萬個任務時,Elixir放棄了,提示 ** (SystemLimitError) a system limit has been reached。編輯:有些評論者指出我可以增加進程限制。在elixir啟動參數中添加--erl '+P 1000000'后,它運行得很好。

ae05cf00-2062-11ee-962d-dac502259ad0.png

圖4:啟動100萬個任務所需的峰值內存

終于我們看到了C#程序內存消耗的增加。但它仍然非常具有競爭力。它甚至成功地稍稍擊敗了Rust的一個運行時!

Go與其他程序之間的差距擴大了?,F在Go在勝利者面前輸掉了超過12倍。它還輸給了Java 2倍以上,這與人們普遍認為JVM是內存大戶,而Go輕量的觀念相矛盾。

Rust的tokio依然無可匹敵。在看過它在100k任務下的表現后,這并不令人驚訝。

最后的話

正如我們觀察到的,大量的并發任務可能會消耗大量的內存,即使它們不執行復雜的操作。不同的編程語言運行時具有不同的取舍,有些在少量任務中表現輕量和高效,但在數十萬個任務中的擴展性表現差。相反,其他一些具有高初始開銷的運行時可以毫不費力地應對高負載。值得注意的是,并非所有運行時都能在默認設置下處理大量的并發任務。

這個比較僅關注內存消耗,而任務啟動時間和通信速度等其他因素同樣重要。值得注意的是,在100萬個任務時,我觀察到啟動任務的開銷變得明顯,大多數程序需要超過12秒才能完成。敬請期待即將到來的基準測試,我將深入探討其他方面。

評論區

評論區也有很多大佬給出了建議,比較有意思,所以也翻譯了放在下方

JB-Dev

在C#實現中,你不需要調用Task.Run(...)。這會增加第二個任務延續的開銷。

在沒有額外開銷的情況下,我觀察到1M基準測試的內存使用量減少了一半以上,從428MB降到183MB(代碼在這里:https://github.com/J-Bax/CS...

例如,不要使用Task.Run(...)而是這樣做:

tasks.Add(Task.Delay(TimeSpan.FromMilliseconds(delayMillisec)));

Christoph Berger

Go的結果并不特別令人驚訝。

每個goroutine開始時的預分配棧為2KiB,所以一百萬個goroutine消耗大約2GB(2,048 * 1,000,000字節)。

這與我使用go build&& /usr/bin/time -l ./goroutinememorybenchmark運行測試代碼時得到的數字非常接近:

2044968960的最大常駐集大小

(我不確定圖中的2,658 GB是如何測量出來的,但數量級是相同的。)

毫無疑問,為每個goroutine預分配一個棧使Go在與那些在真正需要時才分配任何線程本地內存的并發系統的語言相比處于劣勢。(附注:在這特定的上下文中,我將“線程”作為綠色或虛擬線程和goroutine的同義詞。)

我想,在線程做有實質性工作的測試中,各種語言之間的差異可能會大大縮小。

D. Christoph Berge

我不完全了解后面發生了什么,但對于1000個Goroutine,Go只消耗每個goroutine約300字節。只有當它增加到每個3000 Goroutine時,它才開始每個Goroutine使用2KB。

Berger D.

你是如何得到這些測量結果的?

不幸的是,我無法復制你的發現。

為了獲得較小數量的goroutine的更準確結果,我決定在每次測試運行結束后讀取runtime.MemStats.StackInuse。

結果:

所有1000個goroutines完成。StackInuse:2,359,296(每個goroutine 2KB)

所有3000個goroutines完成。StackInuse:6,586,368(每個goroutine 2KB)

所有10000個goroutines完成。StackInuse:21,037,056(每個goroutine 2KB)

所有100000個goroutines完成。StackInuse:205,127,680(每個goroutine 2KB)

所有1000000個goroutines完成。StackInuse:2,048,622,592(每個goroutine 2KB)

Witek

對于Elixir / Erlang,默認進程限制為32k pids。可以通過解釋器標志將其增加到20億。例如:erl +P 4000000,我編寫了一個小的Erlang程序來做你所做的事情(但確保在循環中不分配不必要的內存),并且在1百萬個進程中峰值RSS使用量為2.7GiB。然而,這仍然非常人工和合成。Erlang默認為每個進程分配額外的堆,因為在現實生活中,您實際上會在進程中執行一些操作并需要一點內存,因此預先分配比以后分配更快。如果您真的想在這個愚蠢的基準測試中減少內存使用量,您可以傳遞選項以spawn_opt,或使用自定義+h選項啟動解釋器,例如。+h 10,或者+hms10(默認值為?356)。這將將峰值RSS使用率從2.7 GiB降低到1.1 GiB。

審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 計算機
    +關注

    關注

    19

    文章

    7594

    瀏覽量

    89605
  • 內存
    +關注

    關注

    8

    文章

    3093

    瀏覽量

    74818
  • 編程語言
    +關注

    關注

    10

    文章

    1952

    瀏覽量

    35545
  • 程序
    +關注

    關注

    117

    文章

    3815

    瀏覽量

    81994
  • 多線程
    +關注

    關注

    0

    文章

    279

    瀏覽量

    20189

原文標題:不同語言運行100萬個并發任務需要多少內存?

文章出處:【微信號:TheAlgorithm,微信公眾號:算法與數據結構】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    鴻蒙原生應用開發-ArkTS語言基礎類庫多線程并發概述

    并發模型是用來實現不同應用場景中并發任務的編程模型,常見的并發模型分為基于內存共享的并發模型和基
    發表于 03-22 15:40

    鴻蒙原生應用開發-ArkTS語言基礎類庫多線程并發概述

    并發模型是用來實現不同應用場景中并發任務的編程模型,常見的并發模型分為基于內存共享的并發模型和基
    發表于 03-28 14:35

    為何Python運行效率低?python語言入門

    、Facebook、Redhat、Uber等都在大規模的使用Python完成各種任務!Python的用途越來越廣泛,很受歡迎,必然因為其有很多優點,但是Python也擁有一大缺點,相比于C語言,Python運行
    發表于 02-01 18:47

    并發、并行、進程、線程和協程的區別

    首先是并發,并發的概念是很早就有的了。最早的時候機器都是批處理系統,而且是單道批處理系統,這個系統就是很簡單的線性邏輯,一批處理任務進去,完成,然后進行下面一
    發表于 08-05 08:24

    C語言內存運行時不同變量是怎樣分配的

    C語言內存運行時不同變量是怎樣分配的?怎樣驗證C語言編譯后的內存地址分配是否合理?
    發表于 02-25 06:37

    Lite Actor:方舟Actor并發模型的輕量級優化

    執行的能力,如圖1所示,在一時間段中可能有多個任務都處于已啟動運行運行完畢之間,同時,并發單元也可以在多核設備下并行執行,可以極大地提高
    發表于 07-18 12:00

    ATC'22頂會論文RunD:高密高并發的輕量級 Serverless 安全容器運行時 | 龍蜥技術

    RunD 已經作為 Alibaba 的 Serverless 容器運行時,服務超過100 函數 ,每天調用近 40 億次。在線統計數據表明,RunD 使得每個節點的最大部署密度已
    發表于 09-05 15:18

    移動應用高級語言開發——并發探索

    共享,對基礎架構進行了輕量化處理,大幅提升了啟動時間,且優化了啟動內存。 ArkCompiler并發實例運行 OpenHarmony也提供了TaskPool。TaskPool是一
    發表于 08-28 17:08

    HarmonyOS使用多線程并發能力開發

    代表,不需要開發者去面對鎖帶來的一系列復雜偶發的問題,同時并發度也相對較高,因此得到了廣泛的支持和使用,也是當前ArkTS語言選擇的并發模型。 由于Actor模型的
    發表于 09-25 15:23

    100經典C語言程序

    c語言編寫,c語言100經典程序,單片機的應用,開發利用用
    發表于 12-17 11:46 ?11次下載

    100經典C語言程序資料

    100經典C語言程序資料,感興趣的可以看看。
    發表于 09-27 14:46 ?24次下載

    并發內存池項目實現

    本項目實現了一并發內存池,參考了Google的開源項目tcmalloc實現的簡易版;其功能就是實現高效的多線程內存管理。由功能可知,高并發
    的頭像 發表于 11-09 11:16 ?888次閱讀
    高<b class='flag-5'>并發</b><b class='flag-5'>內存</b>池項目實現

    接口調用并發執行十任務總結

    接口調用時,接收到一列表,十元素,需要并發執行十
    的頭像 發表于 11-15 10:37 ?561次閱讀

    C語言運行環境是什么

    計算等多個領域。為了能夠正確、有效地運行C語言程序,必須具備相應的運行環境。 C語言運行環境包括軟件運行
    的頭像 發表于 11-27 16:13 ?4130次閱讀

    “本源悟空”全球訪問量突破100,已完成14運算任務

    截至2月1日上午11時,我國第三代自主超導量子計算機“本源悟空”已為全球94國家和地區用戶成功完成142233運算任務,全球遠程訪問“悟空”人次已突破100
    的頭像 發表于 02-19 12:50 ?420次閱讀
    “本源悟空”全球訪問量突破<b class='flag-5'>100</b><b class='flag-5'>萬</b>,已完成14<b class='flag-5'>萬</b><b class='flag-5'>個</b>運算<b class='flag-5'>任務</b>
    主站蜘蛛池模板: 国产香蕉一区二区精品视频 | 毛片网站免费在线观看 | 丁香六月婷婷激情 | 天天干免费视频 | 久久久久久久久国产 | 一个色综合网站 | 天天骑夜夜操 | 怡红院日本一道日本久久 | 国产精品福利久久 | 手机看片自拍自自拍日韩免费 | 亚洲人成电影院 | 亚洲日本高清 | 成人v| 亚洲一区二区三区免费在线观看 | 女人aaaaa片一级一毛片 | 日本在线视频不卡 | 亚洲国产精品热久久2022 | 久久精品网站免费观看 | 日产精品卡二卡三卡四卡乱码视频 | 午夜性视频 | 草草影院www色极品欧美 | 好大好猛好爽好深视频免费 | 日本一区二区免费在线观看 | 日木69xxxhd | 男人的天堂黄色 | 免费观看四虎精品国产永久 | 日本黄色片在线播放 | 久久综合免费视频 | 性欧美xxxx视频在线观看 | 五月婷婷丁香综合 | 国产午夜在线观看视频 | 大尺度视频在线 | 欧美性天天影院 | 放荡女同老师和女同学生 | 爱搞逼综合 | 中文字幕在线观看一区二区三区 | 国产三级日产三级日本三级 | 一级毛片免费全部播放 | 在线精品国产第一页 | www.黄黄黄| 日本黄色小视频 |