Go 中的 map 併發存取
Catena (時序儲存引擎)中有一個函式的實現備受爭議,它從 map 中根據指定的 name
獲取一個 metricSource
。每一次插入操作都會至少呼叫一次這個函式,現實場景中該函式呼叫更是頻繁,並且是跨多個協程的,因此我們必須要考慮同步。
該函式從 map[string]*metricSource
中根據指定的 name
獲取一個指向 metricSource
的指標,如果獲取不到則建立一個並返回。其中要注意的關鍵點是我們只會對這個
map 進行插入操作。
簡單實現如下:(為節省篇幅,省略了函式頭和返回,只貼重要部分)
var source *memorySource var present bool p.lock.Lock() // lock the mutex defer p.lock.Unlock() // unlock the mutex at the end if source, present = p.sources[name]; !present { // The source wasn't found, so we'll create it. source = &memorySource{ name: name, metrics: map[string]*memoryMetric{}, } // Insert the newly created *memorySource. p.sources[name] = source }
經測試,該實現大約可以達到 1,400,000 插入/秒(通過協程併發呼叫,GOMAXPROCS
設定為 4)。看上去很快,但實際上它是慢於單個協程的,因為多個協程間存在鎖競爭。
我們簡化一下情況來說明這個問題,假設兩個協程分別要獲取“a”、“b”,並且“a”、“b”都已經存在於該 map 中。上述實現在執行時,一個協程獲取到鎖、拿指標、解鎖、繼續執行,此時另一個協程會被卡在獲取鎖。等待鎖釋放是非常耗時的,並且協程越多效能越差。
讓它變快的方法之一是移除鎖控制,並保證只有一個協程訪問這個 map。這個方法雖然簡單,但沒有伸縮性。下面我們看看另一種簡單的方法,並保證了執行緒安全和伸縮性。
var source *memorySource var present bool if source, present = p.sources[name]; !present { // added this line // The source wasn't found, so we'll create it. p.lock.Lock() // lock the mutex defer p.lock.Unlock() // unlock at the end if source, present = p.sources[name]; !present { source = &memorySource{ name: name, metrics: map[string]*memoryMetric{}, } // Insert the newly created *memorySource. p.sources[name] = source } // if present is true, then another goroutine has already inserted // the element we want, and source is set to what we want. } // added this line // Note that if the source was present, we avoid the lock completely!
該實現可以達到 5,500,000 插入/秒,比第一個版本快 3.93 倍。有 4 個協程在跑測試,結果數值和預期是基本吻合的。
這個實現是 ok 的,因為我們沒有刪除、修改操作。在 CPU 快取中的指標地址我們可以安全使用,不過要注意的是我們還是需要加鎖。如果不加,某協程在建立插入 source
時另一個協程可能已經正在插入,它們會處於競爭狀態。這個版本中我們只是在很少情況下加鎖,所以效能提高了很多
相關推薦
Go 中的 map 併發存取
Catena (時序儲存引擎)中有一個函式的實現備受爭議,它從 map 中根據指定的 name 獲取一個 metricSource。每一次插入操作都會至少呼叫一次這個函式,現實場景中該函式呼叫更是頻繁,並且是跨多個協程的,因此我們必須要考慮同步。 該函式從 map[string]*metricSourc
Go中map的創建和初始化
切片 var 字典 默認 () project key 語義 操作 // code_014_map_usage project main.go package main import ( "fmt" ) func main() { /*Go語言中的map
Go中map的操作與使用(增刪改查)
map aaa 使用 () fun true 增刪改 println 值傳遞 // code_014_map_operator project main.go package main import ( "fmt" ) func DeleteMap(m map[
一段小程式淺析Go中的併發,協程(goroutine),sync.WaitGroup
package main import ( "fmt" "runtime" "sync" ) func main() { runtime.GOMAXPROCS(2) fmt.Println("begin typing") var wg sync.WaitGro
Go語言map的併發操作
先單獨說一下map,在Go語言中,map與通道類似,都需要先用make函式進行初始化,之後才可以賦值,不過map沒有長度的限制,所以在使用make函式初始化map時,make函式的第二個引數不用寫,寫不寫沒有區別。 package main import "fmt" func mai
mapreduce中map和reduce的最大併發數量設定
reduce數量究竟多少是適合的。目前測試認為reduce數量約等於cluster中datanode的總cores的一半比較合適,比如cluster中有32臺datanode,每臺8 core,那麼reduce設定為128速度最快。因為每臺機器8 core,4個作m
go中的資料結構字典-map
1. map的使用 golang中的map是一種資料型別,將鍵與值繫結到一起,底層是用雜湊表實現的,可以快速的通過鍵找到對應的值。 型別表示:map[keyType][valueType] key一定要是可比較的型別(可以理解為支援==的操作),value可以是任意型別。 初始化:map
GO中常用包筆記 bytes(四)
g 學習筆記 bytes包Package bytes對字節數組進行操作的包。功能和strings包相似.bytes包提供的功能有:和另一個字節數組切片的關系(逐字節比較大小,是否相等/相似,是否包含/包含次數,位置搜索,是否是前綴後綴)2.字節數組切片和字符串的關系(字符串中是否含有字節數組所包含的rune,
Python中map和reduce函數
courier ref tail erl position 必須 第一個 http title ①從參數方面來講: map()函數: map()包含兩個參數,第一個是參數是一個函數,第二個是序列(列表或元組)。其中,函數(即map的第一個參數位置的函數)可以接收一個或多個參
python multiprocessing.Pool 中map、map_async、apply、apply_async的區別
pen pool arm res 區別 col apply rgs pytho multiprocessing是python的多進程庫,multiprocessing.dummy則是多線程的版本,使用都一樣。 其中都有pool池的概念,進程池/線程池有共同的方法,其
Go中的結構實現它的的寫法註意事項
pre 值傳遞 為什麽 div 錯誤 寫法 nta () clas 下面一個例子: type Student struct { name string age int } func (s Student) printAge() { fmt.Pr
JAVA中MAP轉LIST
new pub values pac exc except gpo x11 toarray @Test public void testMap2List() throws Exception{ Map<String, String> map = new
mapreduce中map數的測試
1.5 nbsp 啟動 小時 修改 cor core mar 並行 默認的map數是有邏輯的split的數量決定的,根據源碼切片大小的計算公式:Math.max(minSize, Math.min(maxSize, blockSize)); 其中: minsize:默認值:
JavaScript中Map和ForEach的區別
get 新的 create line 速度對比 圖片 技術分享 其中 原來 譯者按: 慣用Haskell的我更愛map。 原文: JavaScript?—?Map vs. ForEach - What’s the difference between Map and Fo
python3中map()和reduce()函數的使用
ada lam map函數 fun name 元組 clas nor 列操作 問題一:利用map()函數,把用戶輸入的不規範的英文名字,變為首字母大寫,其他小寫的規範名字。輸入:[‘adam‘, ‘LISA‘, ‘barT‘],輸出:[‘Adam‘, ‘Lisa‘, ‘Ba
go語言---map
field 傳引用 塊存儲 === 聲明 print ann 維護 ava go語言---map https://blog.csdn.net/cyk2396/article/details/78890185 一.map的用法: type PersonDB struct {
js 中Map使用
遍歷 鍵值 是否 move text each val fun pty function Map() { /** 存放鍵的數組(遍歷用到) */ this.keys = new Array(); /** 存放數據 */
go中的深度拷貝direct版
性能 player int() pen func 拷貝 sting 函數 mar 4.給每個結構寫拷貝函數(效率最高) package main import ( "fmt" "reflect" ) type ( Player struct { Id
GO 中輸出打印的常用函數
字符串 常用函數 %d 輸出 fff nbsp erro 自動增加 不可 1.Println 可以打印字符串和變量(任何類型) println函數在輸出後自動增加一個換行 例: a:=10
go中的make和new的區別
channel 有一點 區別 都是 chan new ron 術語 初始化 適用範圍:make 只能創建內建類型(slice map channel), new 則是可以對所有類型進行內存分配 返回值: new 返回指針, make 返回引用 填充值: new 填充零值,