我一直在嘗試并行化我的 Haskell 代碼,但它變得越來越慢,所以我制作了一些示例代碼來顯示我的問題,這里是串行代碼:
module Main where
import System.Environment
sumRangeSquares :: (Num a, Enum a) => a -> a -> a
sumRangeSquares start end = sum $ map (^2) [start .. end]
main :: IO ()
main = do
[start, end] <- map read <$> getArgs
print $ sumRangeSquares start end
編譯stack ghc -- -O2 -rtsopts -eventlog -threaded src/Main.hs并運行 time ./src/Main 1 10000000,大約0.4秒完成
現在明顯的并行對應物是:
module Main where
import Control.Parallel.Strategies
import System.Environment
sumRangeSquares :: (Num a, Enum a) => a -> a -> a
sumRangeSquares start end = sum $ parMap rseq (^2) [start .. end]
main :: IO ()
main = do
[start, end] <- map read <$> getArgs
print $ sumRangeSquares start end
以相同的方式編譯并運行time ./src/Main 1 10000000 RTS -N4 -lf -s需要超過 6 秒
這是由創建的日志-s:
2,661,959,552 bytes allocated in the heap
1,891,228,032 bytes copied during GC
468,753,512 bytes maximum residency (12 sample(s))
307,102,616 bytes maximum slop
1226 MiB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 1837 colls, 1837 par 10.483s 2.705s 0.0015s 0.0080s
Gen 1 12 colls, 11 par 5.157s 1.391s 0.1159s 0.5573s
Parallel GC work balance: 26.09% (serial 0%, perfect 100%)
TASKS: 10 (1 bound, 9 peak workers (9 total), using -N4)
SPARKS: 10000000 (9998153 converted, 1847 overflowed, 0 dud, 0 GC'd, 0 fizzled)
INIT time 0.038s ( 0.038s elapsed)
MUT time 6.995s ( 2.158s elapsed)
GC time 15.639s ( 4.096s elapsed)
EXIT time 0.001s ( 0.005s elapsed)
Total time 22.673s ( 6.297s elapsed)
Alloc rate 380,577,209 bytes per MUT second
Productivity 30.8% of total user, 34.3% of total elapsed
real 0m6.374s
user 0m16.889s
sys 0m5.859s
這是在 中看到的事件日志threadscope Main.eventlog。

如圖所示,有很多空閑時間,所有四個 HEC 在相對相同的時間運行和空閑。此外,還有很多較長的空閑時間,以及不平衡的火花池和火花創建。
uj5u.com熱心網友回復:
創建一個新的 CPU 執行緒的成本很高,并且您要求為每個微小的計算創建一個新執行緒。兩個整數的乘積比創建一個新執行緒的成本要低得多。所以你的機器忙于創建和殺死新執行緒,而不是做有用的作業。
當你有一個 CPU 時,你必須給它少量昂貴的作業來獲得性能提升。
這可能是尷尬但足夠的示例:我們保留sumRangeSquare與順序變體中的相同并將我們的范圍分成 4 個部分,然后運行 ??4 個并行執行緒sumRangeSquares,然后將 4 個輸出相加得到最終結果。
module Main where
import Control.Parallel.Strategies
import System.Environment
sumRangeSquares :: (Integer, Integer) -> Integer
sumRangeSquares (start, end) = sum $ map (^2) [start .. end]
main :: IO ()
main = do
[start, end] <- map (read :: (String -> Integer)) <$> getArgs
let space = [(start (i-1)*(div (end-start) 4), start i*(div (end-start) 4)) | i <- [1..3]]
print $ sum $ parMap rseq sumRangeSquares (space [(snd $ last space, end)])
我使用 1 和 30 000 000 作為 args 以獲得更重要的結果,我為您準備了這個順序變體:
time ./app/Main 1 30000000
real 0m1,353s
user 0m1,350s
sys 0m0,004s
這對于我的并行,使用一個執行緒運行:
time ./app/Main 1 30000000 RTS -N1 -lf
real 0m1,334s
user 0m1,311s
sys 0m0,022s
這對于我的并行,使用四個執行緒運行:
time ./app/Main 1 30000000 RTS -N4 -lf
real 0m0,416s
user 0m1,386s
sys 0m0,024s
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/494277.html
