閱讀645 返回首頁    go 汽車大全


性能與可伸縮性(第十一章)

性能與可伸縮性

性能包括:服務時間、延遲時間、吞吐率、效率、容量等
可伸縮性:當增加計算資源時(例如CPU、內存、存儲容量或I/O帶寬),程序的吞吐量或者處理能力能相應地增加
當進行性能調優時,其目的通常是用更小的代價完成相同的工作,例如通過緩存來重用之前計算的結果。
當進行可伸縮性調優時,其目的是設法將問題的計算並行化,從而能利用更多的計算資源來完成更多的工作。

在所有並發程序中都包含一些串行部分,如果你認為在你的程序中不存在串行部分,那麼可以再仔細檢查一遍。

1. 線程引入的開銷

  • 上下文切換
  • 內存同步
  • 阻塞

2. 減少鎖的競爭

在並發程序中,對可伸縮性的最主要威脅就是獨占方式的資源鎖。有兩個因素將影響在鎖上發生競爭的可能性:鎖的請求頻率、每次持有該鎖的時間。如果二者的乘積很小,那麼大多數獲取鎖的操作都不會發生競爭,因此在該鎖上的競爭不會對伸縮性造成嚴重影響。

    有3種方式可以降低鎖的競爭程度:
    1. 減少鎖的持有時間
    2. 降低鎖的請求頻率
    3. 使用帶有協調機製的獨占鎖,這些機製允許更高的並發性
  • 縮小鎖的範圍(“快進快出”)

降低發生競爭可能性的一種有效方式就是盡可能縮短鎖的持有時間。例如可以將一些與鎖無關的代碼移出同步代碼塊,尤其是那些開銷較大的操作,以及可能被阻塞的操作,例如I/O

  • 減小鎖的粒度

降低線程請求鎖的頻率(從而減小發生競爭的可能性)。這可以通過鎖分解和鎖分段等技術來實現。在這些技術中將采用多個相互獨立的鎖來保護獨立的狀態變量,從而改變這些變量在之前由單個鎖來保護的情況。
這些技術能減小鎖操作的粒度,並能實現更高的可伸縮性,然而,使用的鎖越多,那麼發生死鎖的風險也就越高。

    如果一個鎖需要保護多個相互獨立的狀態變量,那麼可以將這個鎖分解為多個鎖,並且每個鎖隻保護一個變量,從而提高可伸縮性,並最終降低每個鎖被請求的頻率。
  • 鎖分段

將鎖分解技術進一步擴展為對一組獨立對象上的鎖進行分解,這種情況被稱為“鎖分段”。例如,在ConcurrentHashMap的實現中使用了一個包含16個鎖的數組,每個鎖保護所有散列桶的1/16,其中第N個散列桶由第(N mod 16)個鎖來保護。

    鎖分段的一個劣勢在於:與采用單個鎖來實現獨占訪問相比,要獲取多個鎖來實現獨占訪問將更加困難並且開銷更高(在進行某些操作後,需要獲取所有的鎖)。
  • 避免熱點域

當每個操作都請求多個變量時,鎖的粒度將很難降低。這是在性能與可伸縮性之間相互製衡的另一個方麵。一些常見的優化措施,例如將一些反複計算的結果緩存起來,都會引入一些“熱點域(Hot Field)”,而這些熱點域往往會限製可伸縮性。

  • 一些代替獨占鎖的方法

另一種降低鎖競爭的技術就是放棄使用獨占鎖,從而有助於使用一種友好並發的方式來管理共享狀態。例如,使用並發容器、讀-寫鎖、不可變對象以及原子變量。

  • 向對象池說“不”

3. 減少上下文切換的開銷

在許多任務中都包含一些可能被阻塞的操作。當任務在運行和阻塞這兩個狀態之間切換時,就相當於一次上下文切換。要提高性能,就要減少這樣的切換。

最後更新:2017-11-04 18:33:47

  上一篇:go  顯式鎖(第十三章)
  下一篇:go  避免活躍性危險(第十章)