[英]Mutable, (possibly parallel) Haskell code and performance tuning
我現在已經實現了另一個 SHA3候選者,即Grøstl。 這仍然在進行中(非常如此),但目前224位版本通過了所有KAT。 所以現在我想知道性能(再次: - >)。 這次的不同之處在於,我選擇更接近地鏡像(優化的)C實現 ,即我創建了一個從C到Haskell的端口。 優化的C版本使用表查找來實現該算法。 此外,代碼主要基於更新包含64位字的數組。 因此,我選擇在Haskell中使用可變的無盒載體。
我的Grøstl代碼可以在這里找到: https : //github.com/hakoja/SHA3/blob/master/Data/Digest/GroestlMutable.hs
該算法的簡短描述:它是一個Merkle-Damgård構造,只要有512位的消息塊,就迭代一個壓縮函數(在我的代碼中為f512M )。 壓縮函數非常簡單:它只運行兩個不同的獨立512位排列P和Q (我的代碼中的permP和permQ )並組合它們的輸出。 它的這些排列是由查找表實現的。
Q1)困擾我的第一件事是使用可變向量使我的代碼看起來非常難看。 這是我第一次在Haskell中編寫任何主要的可變代碼,所以我真的不知道如何改進它。 關於如何更好地構建monadic代碼的任何提示都將受到歡迎。
Q2)第二是表現。 實際上它並不太糟糕,因為目前Haskell代碼只慢了3倍。 使用GHC-7.2.1並編譯如下:
ghc -O2 -Odph -fllvm -optlo-O3 -optlo-loop-reduce -optlo-loop-deletion
Haskell代碼使用60秒。 輸入約為1GB,而C版本使用21-22s。 但有一些我覺得奇怪的事情:
(1)如果我嘗試內聯rnd512QM ,代碼需要4倍,但如果我內聯rnd512PM沒有任何反應! 為什么會這樣? 這兩個功能幾乎相同!
(2)這可能更難。 我一直在嘗試並行執行兩個排列。 但目前無濟於事。 這是我嘗試過的一個例子:
f512 h m = V.force outP `par` (V.force outQ `pseq` (V.zipWith3 xor3 h outP outQ))
where xor3 x1 x2 x3 = x1 `xor` x2 `xor` x3
inP = V.zipWith xor h m
outP = permP inP
outQ = permQ m
在檢查運行時統計信息並使用ThreadScope時,我注意到創建了正確數量的SPARKS,但幾乎沒有實際轉換為有用的並行工作。 因此,我在加速方面一無所獲。 我的問題變成了:
非常感謝您的參與。
編輯:
很抱歉沒有提供任何真正的基准測試。 回購中的測試代碼僅供我自己使用。 對於那些想要測試代碼的人,你需要編譯main.hs ,然后運行它:
./main“algorithm”“testvariant”“字節對齊”
例如:
./main groestl short224錯誤
要么
./main groestl e False
( e代表“極端”。這是NIST KATS提供的非常長的消息)。
我檢查了回購,但沒有簡單的基准來運行和玩,所以我的想法只是從眼睛的代碼。 編號與您的問題無關。
1)我很確定force
沒有做你想要的 - 它實際上強制了底層矢量的副本。
2)我認為使用unsafeThaw和unsafeFreeze有點奇怪。 我只是將f512M放入ST monad並完成它。 然后運行它是這樣的:
otherwise = \msg -> truncate G224 . outputTransformation . runST $ foldM f512M h0_224 (parseMessage dataBitLen 512 msg)
3) V.foldM'
有點傻 - 你可以在列表上使用正常(嚴格)foldM - 在第二個參數中折疊向量似乎不買任何東西。
4)我對columnM
的劉海和unsafeReads表示懷疑。
也...
a)我懷疑xoring未裝箱的矢量可能比zipWith
更低的級別zipWith
,利用Data.Vector內部。
b)但是,最好不要這樣做,因為它可能會干擾矢量融合。
c)在檢查時, extractByte
看起來效率不高? 而不是使用fromIntegral來截斷,可以使用mod
或quot
然后使用單個fromIntegral直接轉到Int。
確保使用-threaded -rtsopts
進行編譯並使用+RTS -N2
執行。 沒有它,您將不會有多個OS線程來執行計算。
嘗試激發其他地方引用的計算,否則可能會收集它們:
_
f512 h m = outP `par` (outQ `pseq` (V.zipWith3 xor3 h outP outQ))
where xor3 x1 x2 x3 = x1 `xor` x2 `xor` x3
inP = V.zipWith xor h m
outP = V.force $ permP inP
outQ = V.force $ permQ m
_
3)如果你把事情parseBlock
了,那么parseBlock
接受嚴格的字節parseBlock
(或者在需要的時候使用chunk和pack lazy)然后你可以使用Data.Vector.Storable
並且可能避免一些復制。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.