色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

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

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

3天內不再提示

M7S架構設計內幕

OSC開源社區 ? 來源:OSC開源社區 ? 2023-07-07 16:01 ? 次閱讀

Go語言本身具備出色的性能,然而在流媒體服務器這種CPU密集+IO密集的雙重壓力下,GC帶來的性能損失是最主要的矛盾。而減少GC的操作最直接的辦法就是減少內存申請,多多復用內存。本文將圍繞內存復用這個主題,把M7S中相關技術原理講解一遍,也是M7S性能優化的歷程。

讀寫內存共享

在早期我在研究過許多流媒體服務器的數據轉發模式,基本都是在發送給訂閱者時將內存復制一份的方式實現讀寫分離,雖然沒有并發問題,但是內存頻繁的申請和復制比較消耗資源。

在M7S v1版本中,也沿用了傳統的方式。然而Go語言由于采用GC的方式管理內存,導致頻繁申請內存會加大GC的壓力。

在網友的啟發下,從v2版本開始,采用了基于RingBuffer的內存共享讀寫方式。大大減少了內存復制。

Monibuca中每一個流(Stream)對象包含多個Track(分為音視頻Track和DataTrack)每個Track包含一個RingBuffer發布者將數據填入這個RingBuffer中,訂閱者則從RingBuffer中讀取數據再封裝到協議中發送出去,形成轉發的核心邏輯。

下面的視頻是當時開發的一個UI,實時獲取RingBuffer信息SVG繪制而成。其中發布者正在不斷寫入數據,訂閱者緊隨其后不斷讀取數據。

由于發布者以及訂閱者不在同一個協程中,訪問同一個塊內存很有可能引起并發讀寫的問題。如何解決并發讀寫呢?M7S 經過不斷的迭代在這塊上面實踐了各種方法。既要考慮到性能,還要考慮到代碼的可讀性可維護性。

sync.RWMutex

這是最容易想到的,在M7Sv2中就采用了讀寫鎖。操作步驟如下:

  • 先鎖住Ring中的下一個待寫入單元,再將本次寫完的單元釋放寫鎖。
  • 在本讀寫單元中等待讀取的訂閱者在寫鎖釋放的同時獲取到讀鎖,開始讀取數據
有點類似人走路的方式,前腳著地后,后腳再離地。可以保證訂閱者無法跑到發布者前面。

優點是可讀性很強,一眼就能看懂這個原理。缺點是,鎖的開銷比較大,性能損失很明顯還有一個缺點,就是當訂閱者阻塞,會導致發布者追上訂閱者,寫鎖無法獲取從而阻塞整個流。(后來Go出了TryLock)

WaitGroup

v3 中采用了這個,但是WaitGroupWait操作是一個無限阻塞的操作,必須用Done操作才能結束等待,此時就會有一個問題,engine和發布者有可能會同時去調用Done完成釋放(具體原因另開章節介紹)。因此Done就會多調用一次導致panic。后來通過復雜的原子操作解決了(但是大大降低了代碼的可讀性)。

time.Sleep

v4 中采用了偽自旋鎖,所謂的偽自旋鎖,就是模仿自旋鎖的機制,只是用time.Sleep代替了,runtime.Gosched,減少了自旋次數,從而提高性能。

forr.Frame=&r.Value;r.ctx.Err()==nil&&!r.Frame.CanRead;r.Frame.wait(){
}

CanRead不需要原子操作,有人擔心可能會有并發讀寫問題,其原理同前面說的人走路是一樣的,即便出現了并發讀寫,也不影響邏輯正確運行。最多就是多等待一個周期,稍微增加一點點延遲。

sync.Cond

v1版本中由于使用的是簡單的內存復制,于是有人給了這個方案,但是我卻一直繞了一大圈,最后回到這個方案上了,也算是自作聰明。sync.Cond之所以一開始沒有選擇,是因為里面包含了一個鎖(標準庫內部強制調用了鎖)

func(c*Cond)Wait(){
c.checker.check()
t:=runtime_notifyListAdd(&c.notify)
c.L.Unlock()
runtime_notifyListWait(&c.notify,t)
c.L.Lock()
}

所以就認為性能不高,直到繞了一大圈之后,才找到一個避免鎖的方案。當然這些彎路可能必須要走,因為直到自己寫了偽自旋鎖,才增加了一個是否可讀的屬性,也就是說有了這個屬性后,我們其實只需要一個喚醒的功能即可,于是想到了給sync.Cond提供一個空的鎖對象的方式避免了鎖:

typeemptyLockerstruct{}

func(emptyLocker)Lock(){}
func(emptyLocker)Unlock(){}

varEmptyLockeremptyLocker

sync.Cond在喚醒協程的時候使用的是Broadcast方法,這個方法可以多次調用而無副作用(不像WaitGroupDone方法)。也可以減少偽自旋鎖帶來的輕微延遲。

實際測試中使用Cond比偽自旋鎖大概可以節省10%左右的CPU消耗

協議轉換中的內存復用

協議轉換可以用下面的邏輯來實現:

b3e6dc40-1bf1-11ee-962d-dac502259ad0.png

實際情況比這個要復雜一些。所以這里面第一步需要引入go標準庫中的net.Buffers來表示“連續的內存”(實際并不一定連續)。當收到一個協議傳來的數據時盡量保留,而不去復制它。

同一個協議轉發

對于相同的協議,能復用的內存更多一些,舉個例子:

RTMP轉發到RTMP

RTMP中傳輸視頻幀的格式為AVCC格式,這也是能復用的部分,在實際傳輸過程中這部分內存并非一個連續內存。RTMPchunk機制,會把AVCC切割一塊塊傳輸,并加上chunk header

chunkheader|avccpart1|chunkheader|avccpart2······

這個分割的大小默認是128字節,通常RTMP協議會經過協商修改這個大小,因此傳入和傳出的分塊大小不一定相同。那如何復用AVCC的數據呢?此時我們需要用到net.Buffers來表示一幀AVCC數據。

|avccpart1|avccpart2······

當我們需要另一種分塊大小的數據時,可以對原始數據再分割。比如說原始數據是256字節分塊的:

|256Bytes|256Bytes······

而新的分塊要求是128Bytes的

|128Bytes|128Bytes|128Bytes|128Bytes······

我們并沒有申請新的內存,只是多了一些切片。那有人就可能會問了,如果不是正好倍數關系呢?其實無非就是多切幾塊。比如新的分塊要求是200Bytes:

|200Bytes|56Bytes|144Bytes|112Byts|88Bytes······

用下面的圖更加直觀:

b4025fec-1bf1-11ee-962d-dac502259ad0.png

這樣發送的時候,并不是一個連續內存,那如何發送呢?這里就用到了writev(windows對應的是WSASend)技術。在Go語言中通過net.Buffers類型寫入數據會自動判斷使用的技術。

RTSP轉發到RTSP

RTSP協議傳輸的媒體數據是RTP包,RTP包在理想狀態下,可以完全復用,就是直接把RTP包緩存起來,等需要發送的時候直接把這個RTP數據原封不動的發出去。在m7s中,由于需要有跳幀追幀的邏輯,所以需要修改時間戳,就無法原封不動的發送RTP包,但是也可以復用其中的Payload部分。

HLS轉發到HLS

在純轉發模式下,可以直接將TS切片緩存,完全復用。如果需要將HLS轉換成其他協議,則需要將TS格式數據進行解包處理。

FLV轉發到FLV

FLV格式由于數據格式也是avcc格式,因此處理邏輯就按照avcc格式統一處理了,FLVtag頭無法復用,涉及到時間戳需要重新生成。

不同協議轉發

不同協議之間轉發由于兩兩排列組合很多,因此需要抽象出大類來處理。

協議分類

RTMP、FLV、MP4

該類協議視頻是AVCC格式,音頻是裸格式(RTMP包含一到兩個字節的頭)

RTSP、WebRTC

該類的視頻是RTP(Header+裸NALU)音頻是RTP(Header + AuHeaderLen + AuHeaderxN + AuxN )

HLS、GB28181

這類使用的MPEG2-TSMPEG2-PS作為傳輸協議視頻采用Header+AnnexB音頻采用Header+ADTS+AAC

內存復用

總體而言,視頻格式都是前綴+NALU這種方式,AnnexB的前綴是00 00 00 01,而Avcc的前綴是 CTSNALU長度等,因此將NALU緩存起來就可以復用NALU數據。在實際實現中,為了方便同類型的協議轉換,會同時緩存Avcc格式、RTP格式、以及裸格式,而這三種格式的NALU部分都共用一組內存(內存不連續)

減少發布者的GC

GC的產生

對于一個發布者,即需要不斷從網絡或是本地文件中讀取數據的對象,在不做任何優化的情況下,都會不停的申請內存。例如使用io.ReadAll這種操作,內部會頻繁的申請內存。頻繁申請內存的結果就是GC壓力很大,尤其是高并發的時候,GC帶來的消耗可以達到50%CPU消耗。

b419072e-1bf1-11ee-962d-dac502259ad0.jpg

sync.Pool

當然我最先想到的一定是使用內存池,也就是sync.Pool來管理需要使用的內存,但是sync.Pool有個缺陷,就是為了協程安全內部有鎖。盡管使用了多級緩存等一些列優化手段,最終使用的時候也會消耗一定的性能(經過實測性能開銷很大)。而且sync.Pool比較通用,并不是針對特定的對象使用,我們這里是針對[]byte類型進行復用。

自定義Pool

如果Pool不含有鎖,性能會大幅提升,那如何解決協程安全呢?答案是協程不安全,即我們只在一個協程里面去操作Pool的取出和放回。通常情況下一個發布者的寫入是在同一個協程中的,比如rtmp協議。少數協議如rtsp可能會有多個協程寫入數據,因此最后我們是每一個Track一個Pool,保持一個Track一個協程寫入。

下圖表示的是自定義Pool的結構:

b43108ec-1bf1-11ee-962d-dac502259ad0.png

每個Pool是一個數組,數組的每一個元素是一個鏈表,鏈表的每一個元素是一個包含[]byte的類型,大小是2的數組下標次冪。

0號元素有特殊用途,由于我們需要記錄每一塊內存所屬的鏈表來回收,因此需要有一個外殼,而外殼(ListItem)也是需要回收的。而0號元素是存放的只有外殼需要回收而無需回收Value(需要GC的對象)的鏈表。

typeList[Tany]struct{
ListItem[T]
Lengthint
}
typeListItem[Tany]struct{
ValueT
Next,Pre*ListItem[T]`json:"-"yaml:"-"`
Pool*List[T]`json:"-"yaml:"-"`//回收池
list*List[T]
}
typeBytesPool[]List[Buffer]

回收內存

RingBuffer中的訪問單元被覆蓋時,就可以將其中所有的內存對象進行放回Pool。由此實現了從內存使用的閉環,消除了GC。下圖中紅色箭頭代表內存復用機制,可以有效避免申請內存操作。

b4597d2c-1bf1-11ee-962d-dac502259ad0.png

后記

經過上面三板斧的優化后,整體性能提升了50%以上。下圖測試10000rtmp推流的對比:m7s內存占用較高一些,原因就是采用了內存池來減少GC造成的。使用內存來換CPU,在這種場景下還是值得的。

b473eae0-1bf1-11ee-962d-dac502259ad0.jpg

b489fcf4-1bf1-11ee-962d-dac502259ad0.jpg

b4a265fa-1bf1-11ee-962d-dac502259ad0.jpg

b4c188cc-1bf1-11ee-962d-dac502259ad0.jpg

流媒體服務器 10000路推流CPU消耗
monibuca 90%~100%
zlm 90%~100%
srs 80%~90%
lal 160%~200%

由于livego的推流需要先調用一次HTTP獲取密鑰,所以無法使用壓測工具批量推流,本次對比無法參與。
所有流媒體服務器配置均關閉了協議轉換的開關,并以Release方式編譯。服務器也去除了所有限制,并以完全相同的操作方式進行壓測。



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

    關注

    12

    文章

    9231

    瀏覽量

    85626
  • 架構設計
    +關注

    關注

    0

    文章

    31

    瀏覽量

    6961
  • go語言
    +關注

    關注

    1

    文章

    158

    瀏覽量

    9053

原文標題:方法)。也可以減少偽自旋鎖帶來的輕微延遲。

文章出處:【微信號:OSC開源社區,微信公眾號:OSC開源社區】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    kintex產品架構設計文檔(成為架構師也是電子人不錯的選...

    kintex產品架構設計文檔(成為架構師也是電子人不錯的選擇) ROCE(儒仕),用心為每一位電子人!Xilinx7系列普及講座,架構師設計方案模板,交流學習 內容請下載附件pdf,更
    發表于 04-30 16:41

    軟件架構設計教程

    軟件架構設計教程
    發表于 09-26 15:27

    【汽車電氣架構設計軟件】

    因工作需要,求整車電氣架構設計軟件——PREEvision(盜版),價格可議,WetChat/***,非誠勿擾
    發表于 04-18 14:20

    基于ARM架構設計的M1芯片

    提升巨大,也讓配備 M1 芯片的 Mac 跨入完全不同的層次。由于M1芯片是基于ARM架構設計,所以無法安裝x86版本的Windows。著名的虛擬機軟件parallels desktop推出了基于
    發表于 07-23 09:02

    STM32軟件架構設計的意義

    STM32軟件架構1、架構設計的意義(1)應用代碼邏輯清晰,且避免代碼冗余;(2)代碼通用性,方便軟件高速、有效的移植;(3)各功能獨立,低耦合高內聚;2、總體架構圖3、結構層說明4、遵循規則5、優劣評估6、STM32實例說明
    發表于 08-04 07:23

    為何要進行嵌入式軟件架構設計?如何設計?

    為何要進行嵌入式軟件架構設計?如何進行嵌入式軟件架構設計?
    發表于 11-01 06:31

    對嵌入式系統中的架構設計的理解

    【閱讀這篇文章,你能了解到什么】1. 從事嵌入式開發12年的我,對架構設計的理解;2. 對嵌入式系統中的架構設計要刻意訓練;3. 嵌入式系統開發過程中的一些小技巧;4. 一個用于智能家居項目
    發表于 11-08 08:23

    基于ARMCortex_M3核的SoC架構設計及性能分析

    基于ARMCortex_M3核的SoC架構設計及性能分析
    發表于 09-29 09:26 ?18次下載
    基于ARMCortex_<b class='flag-5'>M</b>3核的SoC<b class='flag-5'>架構設</b>計及性能分析

    富可視發布全面屏新機“M7s”:前后攝像頭同時拍攝

    富可視(InFocus)近日在臺灣發布了一款全面屏新機“M7s”,配置不算很高但也頗有特點,性價比相當出色。 該機采用了三段式機身設計,后置指紋,重量148克,厚度8.8毫米,提供曜石黑、鉑光金兩種
    發表于 01-29 14:27 ?524次閱讀

    ARMv7-M嵌入式架構的特點是什么

    ARM Cortex-M處理器系采用ARMv7-M架構設定,Cortex-M0和Cortex-M0+則采用類似的ARMv6-
    發表于 10-22 14:32 ?4892次閱讀

    系統架構設計的詳細講解

    上一篇,我們討論了故障度量和安全機制的ASIL等級。本篇我們來聊一聊系統架構設計相關內容。01系統架構設計和TSC當我們開始寫TSC時,會涉及到下圖中一系列的內容:當我們完成前三期(鏈接見文末)提到的安全機制規范后,我們就要開始整理好所有的安全需求并在系統
    的頭像 發表于 12-24 14:33 ?1740次閱讀

    SWE.2的軟件架構設

    過程ID:SWE.2 過程名稱:軟件架構設計 過程目的:軟件架構設計過程目的是建立一個架構設計,識別哪些軟件需求應該分配給軟件的哪些要素,并根據已定義的標準評估軟件架構設計。 ? 過程
    的頭像 發表于 01-11 10:36 ?2782次閱讀

    SYS.3的系統架構設

    系統架構設計 過程ID:SYS.3 過程名稱:系統架構設計 ? 過程目的:系統架構設計過程目的,是建立系統架構設計,并確定將哪些系統需求分配給系統的哪些要素,以及根據已定義的準則評估系
    的頭像 發表于 02-13 16:02 ?2713次閱讀

    架構與微架構設

    下面將從芯片的架構設計、微架構設計、使用設計文檔、設計分區、時鐘域和時鐘組、架構調整與性能改進、處理器微架構設計策略等角度進行說明,并以視頻H.264編碼器設計為例。
    的頭像 發表于 05-08 10:42 ?1217次閱讀
    <b class='flag-5'>架構</b>與微<b class='flag-5'>架構設</b>計

    SWE.2軟件架構設

    過程ID : SWE.2 過程名稱 : 軟件架構設計 過程目的 : 軟件架構設計過程目的是建立一個架構設計,識別哪些軟件需求應該分配給軟件的哪些要素,并根據已定義的標準評估軟件架構設
    的頭像 發表于 08-24 09:43 ?953次閱讀
    主站蜘蛛池模板: 3DNagoonimation动漫| 99国内偷揿国产精品人妻| 欧美日韩精品一区二区三区高清视频| 狠狠色综合久久丁香婷婷| 国产精品18久久久久久欧美| oldgrand欧洲老妇人| 97人妻精品全国免费视频| 中文字幕乱码一区久久麻豆樱花| 亚洲免费观看在线视频| 亚洲 日韩 色 图网站| 午夜宅宅伦电影网| 乌克兰10一12x video| 帅哥男男GV在线1080P| 色狗综合网| 视频在线免费观看| 天天躁日日躁狠狠躁中文字幕老牛| 色一伦一情一区二区三区| 乳巨揉みま痴汉电车中文字幕动漫| 日本免费一区二区三区最新vr| 秋霞电影网午夜鲁丝片无码| 秋霞特色大片18岁入口| 日韩精品久久久久影院| 色多多涩涩屋下载软件| 偷拍亚洲制服另类无码专区| 呜呜别塞了啊抽插| 亚洲AV国产福利精品在现观看| 亚洲国产AV精品一区二区蜜芽| 亚洲精品成人无码A片在线| 亚洲欧美激情精品一区二区| 伊人狼人久久精品热9| 2020久久精品永久免费| 99热在线观看精品| 成人亚洲精品| 国产无遮挡又黄又爽在线视频| 狠狠久久免费视频在线| 久久视频在线视频| 欧美激情视频二区| 熟妇久久无码人妻AV蜜桃| 亚洲H成年动漫在线观看不卡| 夜夜国产亚洲视频香蕉| 97色伦图片7778久久|