欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

go?time.After優(yōu)化后性能提升34%內(nèi)存減少67%

 更新時(shí)間:2023年02月24日 14:39:00   作者:steden  
這篇文章主要介紹了go語(yǔ)言time.After優(yōu)化后性能提升34%內(nèi)存減少67%實(shí)例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

大家好,今天給大家?guī)?lái)一篇如何優(yōu)化time.After函數(shù)。

最近我在做調(diào)度中心2.0的重構(gòu)。本次重構(gòu)使用的GO語(yǔ)言開(kāi)發(fā)。

在項(xiàng)目中,基本都離不開(kāi)需要休眠等待一定時(shí)間后再執(zhí)行下一步邏輯的操作,再搭配select,用起來(lái)是真的舒服。

func waitWorking() {
    select {
    case <-time.After(5 * time.Second): // 每隔5秒,主動(dòng)向客戶端詢問(wèn)任務(wù)狀態(tài)
        _ = receiver.CheckWorkingEventBus.Publish(receiver)
    case <-receiver.updated:
    }
}

在這個(gè)示例中,5秒后會(huì)執(zhí)行Publish函數(shù),或者<-receiver.updated有數(shù)據(jù)時(shí)退出,這是我們比較常用的方式。

但有一點(diǎn)要注意的是:time.After如果沒(méi)有被執(zhí)行到,會(huì)導(dǎo)致無(wú)法第一時(shí)間GC回收內(nèi)存。

從內(nèi)存分析中,會(huì)看到內(nèi)存在持續(xù)增長(zhǎng),到了一定時(shí)間后,才會(huì)下降。這個(gè)增長(zhǎng)幅度隨著你的項(xiàng)目請(qǐng)求量而決定。

這是因?yàn)楫?dāng)<-receiver.updated被觸發(fā)執(zhí)行時(shí),導(dǎo)致time.After(5 * time.Second)在5秒后才會(huì)有數(shù)據(jù)進(jìn)來(lái),在這5秒內(nèi),time.After創(chuàng)建的NewTimer(d)是無(wú)法回收的。

func After(d Duration) <-chan Time {
    return NewTimer(d).C
}

明白了這一點(diǎn)之后,我們可以簡(jiǎn)單的做一個(gè)改進(jìn)

改進(jìn)1:

func waitWorking() {
    timer := time.NewTimer(5 * time.Second)
    select {
    case <-timer.C: // 每隔5秒,主動(dòng)向客戶端詢問(wèn)任務(wù)狀態(tài)
        _ = receiver.CheckWorkingEventBus.Publish(receiver)
    case <-receiver.updated:
        timer.Stop()
    }
}

當(dāng)<-receiver.updated被觸發(fā)執(zhí)行時(shí),我們主動(dòng)調(diào)用Stop方法,來(lái)告知GC,此timer對(duì)象不再使用。

這樣就不至于等到5秒后,GC才知道這個(gè)對(duì)象不再使用。

這就完了嗎?顯示沒(méi)有,如果waitWorking函數(shù)會(huì)在并發(fā)中被調(diào)用:

type TaskGroupMonitor struct {
    updated chan struct{}   // 數(shù)據(jù)有更新,讓流程重置
   name    string          // 任務(wù)名稱
}
func (receiver *TaskGroupMonitor) waitWorking() {
    timer := time.NewTimer(5 * time.Second)
    select {
    case <-timer.C: // 每隔5秒,主動(dòng)向客戶端詢問(wèn)任務(wù)狀態(tài)
        _ = receiver.CheckWorkingEventBus.Publish(receiver)
    case <-receiver.updated:
        timer.Stop()
    }
}

func init() {
   // 模擬數(shù)據(jù)庫(kù)讀到了100條任務(wù)
    for i := 0; i < 100; i++ {
      taskGroup:= TaskGroupMonitor{}
        go taskGroup.waitWorking()
    }
}

這里假如從數(shù)據(jù)庫(kù)中讀到了100條任務(wù)數(shù)據(jù),每條數(shù)據(jù)都在獨(dú)立的協(xié)程中運(yùn)行。

這就會(huì)導(dǎo)致在這100條任務(wù)在運(yùn)行的過(guò)程中,創(chuàng)建了100個(gè)time.Timer對(duì)象,事實(shí)上除了waitWorking,還會(huì)有waitStart,waitScheduler,taskFinish等函數(shù)也使用了time.Timer對(duì)象。

可以想到,項(xiàng)目在運(yùn)行過(guò)程中time.Timer在不停的創(chuàng)建,直到GC后才被回收。這將導(dǎo)致我們的內(nèi)存一直占用著。

并且time.Aftertime.NewTicker并不是高精度的時(shí)間控制。有時(shí)候會(huì)慢那么0-3ms,協(xié)程數(shù)量越多越繁忙,則越不精準(zhǔn)。

這對(duì)于調(diào)度中心而言是無(wú)法接收的,我的目標(biāo)是支持幾千個(gè)任務(wù)同時(shí)監(jiān)控調(diào)度。意味著協(xié)程數(shù)量會(huì)非常高。

而在GO的time.Timer中是使用64個(gè)timersBucket,并使用四叉堆來(lái)管理各個(gè)timer,雖然在1.17版本有所改進(jìn)。

但時(shí)間上仍然沒(méi)有那么準(zhǔn)確,對(duì)于調(diào)度這種場(chǎng)景來(lái)說(shuō),對(duì)ms級(jí)別的延遲也是沒(méi)辦法接受的。

time.Timer原理不在本篇的范圍內(nèi),現(xiàn)在有很多大神有這方面的剖析,感興趣可以去搜搜。

分析問(wèn)題

通過(guò)簡(jiǎn)單的分析,我們已經(jīng)知道使用time.Timer會(huì)有如下缺點(diǎn):

  • 每個(gè)協(xié)程需要?jiǎng)?chuàng)建time.Timer(導(dǎo)致內(nèi)存占用上升)
  • time.Timer會(huì)有延遲(對(duì)于ms敏感的場(chǎng)景不適用)

即如此,我們是否可以通過(guò)統(tǒng)一的時(shí)間管理器來(lái)管理所有的時(shí)間觸發(fā)器呢?

答案是顯而易見(jiàn)的,那就是時(shí)間輪。

時(shí)間輪

時(shí)間輪是一種實(shí)現(xiàn)延遲功能的算法, 它在Linux內(nèi)核中使用廣泛, 是Linux內(nèi)核定時(shí)器的實(shí)現(xiàn)方法和基礎(chǔ)之一. 時(shí)間輪是一種高效來(lái)利用線程資源來(lái)進(jìn)行批量化調(diào)度的一種調(diào)度模型, 把大量的調(diào)度任務(wù)全部綁定到同一個(gè)調(diào)度器上, 利用這個(gè)調(diào)度器來(lái)進(jìn)行所有任務(wù)的管理, 觸發(fā)以及運(yùn)行.

簡(jiǎn)單來(lái)說(shuō),時(shí)間輪就是一個(gè)模擬時(shí)鐘的原理。 實(shí)現(xiàn)方式有:?jiǎn)螌印㈦p層、多層三種方式。

而在雙層、多層時(shí)間輪中,又有兩種算法:一種是不管幾層,時(shí)間周期是一樣的。另一種是低層一圈 = 上層一格(像秒針、分針一樣)

在時(shí)鐘里,秒針走完一圈,分針走一格。分針走完一圈,時(shí)針走一格。以此類推。

當(dāng)秒針走到第X格,會(huì)到第X格的隊(duì)列中找到是否有待執(zhí)行任務(wù)列表,如果有則取出并通知到C變量。

而我在實(shí)現(xiàn)這個(gè)時(shí)間輪就是完全模擬時(shí)鐘的這種算法來(lái)實(shí)現(xiàn)的。我與其它開(kāi)源的時(shí)間輪不一樣的地方是,我是高精度算法的。

時(shí)間輪的原理大概就講這么多,畢竟不是一個(gè)什么新鮮的算法,網(wǎng)上有很多講的比我更透徹的大神,在這里我主要講使用時(shí)間輪的前后對(duì)比。

我們來(lái)看看如何使用:

// 在項(xiàng)目中,定義一個(gè)全局變量tw,并規(guī)定第0層,走一格=100ms,一圈有120格
import "github.com/farseer-go/fs/timingWheel"

var tw = timingWheel.New(100*time.Millisecond, 120)
tw.Start()

接著在項(xiàng)目中我們改成時(shí)間輪來(lái)控制時(shí)間:

func (receiver *TaskGroupMonitor) waitWorking() {
   select {
   case &lt;-tw.AddPrecision(60 * time.Second).C:
        _ = receiver.CheckWorkingEventBus.Publish(receiver)
   case &lt;-receiver.updated:
   }
}

至此,我們使用了全局tw變量來(lái)控制時(shí)間的延遲管理。

我們來(lái)看下,優(yōu)化前的情況:

100個(gè)并發(fā)下調(diào)度:平均延遲:10ms、CPU:31.4%、內(nèi)存:115m

優(yōu)化后:

100個(gè)并發(fā)下調(diào)度:平均延遲:1ms、CPU:21.7%、內(nèi)存:34.5m

為此,整體性能提升:34%,內(nèi)存減少:67%

相關(guān)材料:

以上就是go time.After優(yōu)化后性能提升34%內(nèi)存減少67%的詳細(xì)內(nèi)容,更多關(guān)于go time.After優(yōu)化性能內(nèi)存的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語(yǔ)言的常量、枚舉、作用域示例詳解

    Go語(yǔ)言的常量、枚舉、作用域示例詳解

    這篇文章主要介紹了Go語(yǔ)言的常量、枚舉、作用域,接下來(lái),我們將詳細(xì)了解 Go 的變量作用域規(guī)則以及這些規(guī)則如何影響代碼編寫(xiě),需要的朋友可以參考下
    2024-07-07
  • 淺析Gin框架中路由參數(shù)的使用

    淺析Gin框架中路由參數(shù)的使用

    這篇文章主要為大家介紹了路由參數(shù)的基本語(yǔ)法,以及路由匹配和路由參數(shù)值提取等相關(guān)內(nèi)容,以幫助讀者更好地對(duì)Gin?框架中路由參數(shù)進(jìn)行使用,需要的可以參考下
    2023-08-08
  • VSCode必裝Go語(yǔ)言以下插件的思路詳解

    VSCode必裝Go語(yǔ)言以下插件的思路詳解

    這篇文章主要介紹了VSCode必裝Go語(yǔ)言以下插件的思路詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • golang 語(yǔ)言中錯(cuò)誤處理機(jī)制

    golang 語(yǔ)言中錯(cuò)誤處理機(jī)制

    Golang 的錯(cuò)誤處理方式可能和這些你熟悉的語(yǔ)言有所不同,今天通過(guò)本文給大家分享golang 語(yǔ)言中錯(cuò)誤處理機(jī)制,感興趣的朋友一起看看吧
    2021-08-08
  • go程序中同一個(gè)包下為什么會(huì)存在多個(gè)同名的函數(shù)或變量(詳細(xì)解析)

    go程序中同一個(gè)包下為什么會(huì)存在多個(gè)同名的函數(shù)或變量(詳細(xì)解析)

    這篇文章主要介紹了go程序中同一個(gè)包下為什么會(huì)存在多個(gè)同名的函數(shù)或變量(詳細(xì)解析),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-05-05
  • Go?Web編程添加服務(wù)器錯(cuò)誤和訪問(wèn)日志

    Go?Web編程添加服務(wù)器錯(cuò)誤和訪問(wèn)日志

    這篇文章主要為大家介紹了Go?Web編程添加服務(wù)器錯(cuò)誤日志和訪問(wèn)日志的示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Golang使用Token來(lái)驗(yàn)證

    Golang使用Token來(lái)驗(yàn)證

    token指的是一種用于驗(yàn)證用戶身份或授權(quán)訪問(wèn)的憑證,本文主要介紹了Golang使用Token來(lái)驗(yàn)證,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-08-08
  • Golang中crypto/ecdsa庫(kù)實(shí)現(xiàn)數(shù)字簽名和驗(yàn)證

    Golang中crypto/ecdsa庫(kù)實(shí)現(xiàn)數(shù)字簽名和驗(yàn)證

    本文主要介紹了Golang中crypto/ecdsa庫(kù)實(shí)現(xiàn)數(shù)字簽名和驗(yàn)證,將從ECDSA的基本原理出發(fā),詳細(xì)解析如何在Go語(yǔ)言中實(shí)現(xiàn)數(shù)字簽名和驗(yàn)證,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • Golang標(biāo)準(zhǔn)庫(kù)binary詳解

    Golang標(biāo)準(zhǔn)庫(kù)binary詳解

    這篇文章主要介紹了Golang標(biāo)準(zhǔn)庫(kù)binary的相關(guān)資料,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • 值得擁有的Go語(yǔ)言常用內(nèi)置包使用示例

    值得擁有的Go語(yǔ)言常用內(nèi)置包使用示例

    本文將介紹?Go?語(yǔ)言的常用內(nèi)置包,詳細(xì)介紹它們的功能、用法,并通過(guò)通俗易懂的示例代碼,讓讀者掌握這些內(nèi)置包的精髓,提高?Go?語(yǔ)言編程技能
    2023-11-11

最新評(píng)論