騰訊開源的 libco 號稱千萬級協程支援,那個共享棧模式原理是什麼

時間 2021-05-12 05:00:13

1樓:owen

個人認為是共享棧的優點是節省了記憶體資源,缺點就是memcopy棧開銷吧。

執行過程其實就是協程的stack放到執行stack裡,切換的時候,先把在執行stack的協程stack儲存下,放到乙個地方,再把其他協程stack放到執行stack裡。

2樓:戈君

這種「共享棧」使用場景很侷限,與其這樣還不如直接寫全非同步程式了,空間、效率都更加好,用這種協程有點脫褲子放屁的感覺。

通過hack系統庫來支援應用層我也是不信的,系統一公升級就各種問題,而且改動底層的那些函式很容易死鎖,這麼改就是把系統繫結在乙個基本不公升級的作業系統,個人做成了專案,但對系統長期是有害的。

協程這些東西不用去迷信,本身並不複雜,也沒有什麼黑魔法,重點還是降低開發成本,降低系統複雜度,同時能承接越來越複雜的現實問題。

3樓:Ivony

看了半天,原來就是棧空間共享,切換的時候用記憶體拷貝把棧內容給拷貝到共享的棧空間來??

我還以為會有棧底復用這樣高階的技術呢……

我知道很多人會看得雲裡霧裡,雖然我不保證我的理解一定正確,但如果我理解正確的話,這個事情非常簡單。

你只要搞明白一件事情,不管是執行緒還是協程(有棧的),棧空間通常是預留了乙個固定大小的。

在棧上面分配變數啊引數什麼東西,就是把棧頂指標往後面推。這樣做的好處是在棧上分配和釋放速度巨快,因為直接把棧頂指標往前推往後推就完事了。

但最大的弊病就是我們得預留一大塊記憶體來作為棧空間,這一塊記憶體不能挪作他用。棧也很難動態擴容,因為這涉及到棧位址變化,棧指標都會掛掉。當在棧上分配記憶體導致棧頂指標超出了棧空間預留大小的時候,就是喜聞樂見的爆棧了(StackOverflow)。

而共享棧說白了就是棧空間是同一塊記憶體位址,每次切換的時候都把棧空間備份和還原……好處是備份還原的時候不是整個棧空間,只是被用到的空間,所以可以省記憶體。

4樓:暗淡了烏雲

共享棧是為了克服靜態棧的缺點,通常來說,靜態棧會給每個協程分配乙個固定的大小記憶體空間作為棧空間,這個空間如果太小,容易不夠用溢位,如果分配太多,在協程很多的情況下會太消耗記憶體。因此出現了共享棧,就是預先分配一塊大記憶體作為所有協程的共享執行時棧空間,而每個協程還有乙個私有的棧空間,這個空間的大小是占用多少分配多少的,乙個協程執行,那麼把其棧空資料拷貝到執行棧空間上,協程切換出來的時候再從共享的執行時棧空間拷貝到自己私有的棧空間,有多大就分配多大空間用於存放。考慮到競爭,一般會有多個預先分配的協程共享執行時棧空間,按照一定的規則將所有協程對映到固定的共享棧上就可以了。

這樣除了幾個共執行時棧空間稍微大些,協程私有的棧空間都是占有多少分配多少。記憶體節省了,但是效能會稍差,協程切換需要棧空間資料拷貝,其實開銷也不小。

考慮到64位環境,程序虛擬位址空間充足,可以使用虛擬記憶體棧,Virtual Memory Stack,充分利用程序申請的記憶體並不會立即被對映成物理記憶體,而是僅管理於虛擬記憶體中,真正對其讀寫時會觸發缺頁中斷,此時才會對映為物理記憶體的特性,大可直接給協程在堆上分配較大的棧空間,在libgo, bthread 中都是預設分配1MB的堆上空間~

5樓:zyzacz

很贊同前面鍾宇騰,陳華,Mr. li, cholerae, 江哈莫夫斯基等人的回答,基本就是不停malloc不停free棧空間,以及不停地copy in不停copy out當前棧,然後呢?根據這個東西自己也寫了個小玩意,原理一模一樣。

github上求不噴:Yuandong-Chen/ezCoroutine

6樓:江哈莫夫斯基

libco的切換要更快。

一般用ucontext實現的coroutine, 切換基於glibc的swapcontext.S

主要儲存和恢復以下部分:

棧暫存器

浮點環境(不需要)

signal mask(不需要)

其中signal mask會進行system call,會陷入核心,從而引起核心態和使用者態的上下文切換,這個是最主要的效能瓶頸。

此外,在網路應用中,通常也不需要浮點環境,也可以去掉。

之前做過乙個簡單的對比實驗:

預設版本:

每秒1.99 Million Context Switches在swapcontext.S中去掉浮點環境和 signal mask部分:

每秒7.22Million Context Switchescoctx_swap.S裡面,省掉了上面的浮點環境和signal掩碼。

從這個角度看,libco的實現,是乙個優化版。

7樓:

寫了swap的過程,可能跟題主的問題不太相關。不過可以科普一下兩個協程如何進行執行時切換的過程。

8樓:

這個其實是作者寫的太玄乎,試想作業系統中有500個任務,而系統只有固定的255個tss段,怎麼辦?分時復用,當前被排程的任務可以獲得乙個TSS段,然後把自己的TSS內容拷貝到那個TSS段,等到這個程序的時間用完,就把自己的TSS拷貝到一邊儲存起來。下乙個被排程的再拷貝進來。

就是乙個碗10個人用來吃飯。

Windows和Linux的任務排程都是這個思想和實現。

9樓:歐文韜

按現在的memcpy的效能計算,大約用60k的棧的話,單核的switch效能在每秒10萬的級別。所以還是要看場景,如果乙個協程裡少量create,頻繁yield和resume,並且棧深度如果通常比較高,還是不共享比較好。

另外乙個共享棧的問題就是不同的協程之間不能互相引用對方棧上的資料。不過這個場景不多就是了。

10樓:cholerae

其他答案都說了節省空間的問題,其實共享棧還解決了乙個問題,就是協程的使用者不用再擔心棧的大小問題,就是不用擔心棧太小爆掉的問題了,因為協程的棧不是單獨的、固定大小的。

libco 的可讀性我覺得一般,雲風之前寫過乙個協程庫,也是用的共享棧,只實現了最基本的 create/resume/yield/status 原語,我覺得可讀性要好得多,可以參考:GitHub - cloudwu/coroutine: A asymmetric coroutine library for C.

11樓:呵呵一笑百媚生

大致看了一下 @鍾宇騰 的分析,我的理解就是時間換空間。

有時人們為了降低CPU時間,大量用記憶體池的方式替代系統malloc/new,空間換時間;而這裡,為了在物理記憶體不變的情況下,能夠支援更大的併發量,用malloc/free代替預分配的固定尺寸棧空間,時間換空間。

我覺得第乙個提出共享棧這種想法的人,絕對算是創新的優化。

棧空間的使用率,是在執行時動態變化的,所以需要預分配足夠大的一塊連續記憶體,這就存在記憶體浪費的問題。當併發協程數量達到千萬級的時候,這個記憶體浪費還是相當可觀的。

另外,考慮協程排程的特點。一般情況下協程排程都是在數量不多的作業系統執行緒,甚至是單執行緒中進行的。所以同乙個作業系統執行緒裡的大量協程,每時每刻只有乙個在被排程執行,所有其他協程都在佇列裡休眠/掛起。

只有那個正在執行的協程,它是真的在使用那個空間相當大的執行棧;所有其他掛起協程,他們的執行棧都沒在使用,唯一的作用只是儲存區域性空間資料,而且資料量在休眠的時候是確定的。還有一點很重要,協程的排程是可控的。

基於上面這兩點,共享棧這個優化切實可行,對節省記憶體也很有效。無法避免的,會增大CPU開銷:動態私有記憶體分配、記憶體拷貝。

如何看待騰訊開源的 TSW js?

在鵝廠sng的大環境下能沉下心做點技術並且開源出來已經很不容易了,實名反對那個被高工指引去看文件就玻璃心碎一地的,批評專案可以,但請不要參雜人參公雞 郭凱 不忍吐槽,README也真的是言簡意賅,初步看了下原始碼 雖然原始碼可讀性並不強 感覺就是乙個LOGGER中介軟體 雲儲存 一堆常見的Node運...

如何評價騰訊開源的PHP框架Biny?

很多回答都在說第三方包相關的事情,什麼composer啊namespace啊,我覺得,很可能人家一開始的初衷就是全部造輪子盡量不用第三方庫呢?不是所有人都習慣於一大堆包,管都管不過來,不得不求助於專門的管理程式的。 fa2002 千萬不要用很多都是為了評級搞的噱頭那天負責人換了或是部門職能換了專案就...

MySQL 對於千萬級的大表要怎麼優化?

架構師 每當看到這種問題,總有一大堆人在下邊回答不搭邊的東西,從網上copy 幾萬字的內容對於問題有意義嗎?言歸正傳,mysql單錶到達千萬級別確實會有效能瓶頸,不的不說免費的mysql效能和sqlserver,oracel差的太多了。至於優化,可以做一下幾點 1。網上都寫爛的分庫分表,但是也要根據...