我正在編寫一個程序,其中大量的代理偵聽事件並對它們做出反應。由於Control.Concurrent.Chan.dupChan
已被棄用,我決定使用TChan作爲廣告。性能差/與STM鎖定
TChan的表現比我預期的要糟糕得多。我有以下程序說明了問題:
{-# LANGUAGE BangPatterns #-}
module Main where
import Control.Concurrent.STM
import Control.Concurrent
import System.Random(randomRIO)
import Control.Monad(forever, when)
allCoords :: [(Int,Int)]
allCoords = [(x,y) | x <- [0..99], y <- [0..99]]
randomCoords :: IO (Int,Int)
randomCoords = do
x <- randomRIO (0,99)
y <- randomRIO (0,99)
return (x,y)
main = do
chan <- newTChanIO :: IO (TChan ((Int,Int),Int))
let watcher p = do
chan' <- atomically $ dupTChan chan
forkIO $ forever $ do
[email protected](p',_counter) <- atomically $ readTChan chan'
when (p == p') (print r)
return()
mapM_ watcher allCoords
let go !cnt = do
xy <- randomCoords
atomically $ writeTChan chan (xy,cnt)
go (cnt+1)
go 1
在編譯時(O),並運行程序首先會輸出是這樣的:
./tchantest ((0,25),341) ((0,33),523) ((0,33),654) ((0,35),196) ((0,48),181) ((0,48),446) ((1,15),676) ((1,50),260) ((1,78),561) ((2,30),622) ((2,38),383) ((2,41),365) ((2,50),596) ((2,57),194) ((3,19),259) ((3,27),344) ((3,33),65) ((3,37),124) ((3,49),109) ((3,72),91) ((3,87),637) ((3,96),14) ((4,0),34) ((4,17),390) ((4,73),381) ((4,74),217) ((4,78),150) ((5,7),476) ((5,27),207) ((5,47),197) ((5,49),543) ((5,53),641) ((5,58),175) ((5,70),497) ((5,88),421) ((5,89),617) ((6,0),15) ((6,4),322) ((6,16),661) ((6,18),405) ((6,30),526) ((6,50),183) ((6,61),528) ((7,0),74) ((7,28),479) ((7,66),418) ((7,72),318) ((7,79),101) ((7,84),462) ((7,98),669) ((8,5),126) ((8,64),113) ((8,77),154) ((8,83),265) ((9,4),253) ((9,26),220) ((9,41),255) ((9,63),51) ((9,64),229) ((9,73),621) ((9,76),384) ((9,92),569) ...
然後,在某個時候,將停止寫任何東西,同時仍然消耗100%的CPU。
((20,56),186) ((20,58),558) ((20,68),277) ((20,76),102) ((21,5),396) ((21,7),84)
隨着-threaded死機甚至更快,僅線屈指可數後發生。它也將消耗通過RTS'-N標誌提供的任何數量的內核。
此外,性能似乎相當差 - 每秒只能處理約100個事件。
這是STM中的錯誤還是我誤解了STM的語義?
你誤解的一件事是'Chan'會喚醒一個閱讀器,而STM的'TChan'會喚醒每個單獨寫入的所有*閱讀器。除此之外,尼爾布朗在他的回答中對你有很好的建議。 –
這不是你誤解的STM的語義,而是實現。它實施了樂觀鎖定。這使得它適用於有許多獨立的可變單元格和許多要更新通常不重疊的子集的事務。在每次交易觸及同一個可變單元格的情況下(如本例中的TChan),這也是非常不合適的。 – Carl
即使在每個事務觸及相同的可變單元的情況下,只要讀操作足夠支配寫操作,就可以做得非常好。 – sclv