我正在尝试减少程序中的GC时间。主要嫌疑人是以下代码:
Data.Vector.Unboxed.fromList . take n . List.sortBy (flip $ Ord.comparing id)
$ [ ( sum [ (c + a) * wsum z | (z,c) <- IntMap.toList zt_d ] , d)
| d <- IntMap.keys $ m
, let zt_d = IntMap.findWithDefault IntMap.empty d $ m ]
正在排序的列表通常包含数千个元素。我认为列表排序是罪魁祸首,因为如果我将take n . List.sortBy (flip $ Ord.comparing id)
替换为return . List.maximum
,我的工作效率将从60%提高到95%。
我可以做些什么来减少分配吗?
更新
根据建议,我使用vector-algorithms
的原位排序替换了List.sort。
也许我做错了,但我看到的是没有分配(生产率为97%而不是列表的63%),但程序慢了很多:它使用List.sortBy在85秒内运行;以原地排序我杀了它之后
等了7分钟。我尝试了Intro和Merge的排序。这是我的代码:
import qualified Data.Vector.Generic.Mutable as GM
import qualified Data.Vector.Generic as G
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Algorithms.Merge as Sort
import qualified Data.Vector.Fusion.Stream as Stream
import Control.Monad.ST
sortBy :: (Ord a, U.Unbox a) => (a -> a -> Ordering) -> [a] -> U.Vector a
sortBy cmp xs = runST $ do
mv <- GM.unstream . Stream.fromList $ xs
Sort.sortBy cmp mv
G.unsafeFreeze mv
答案 0 :(得分:2)
排序确实看起来确实会导致很多分配。虽然排序是在列表上执行的,但是无法完全更改,因为排序列表会导致构建许多中间列表。如有必要,您可以尝试使用例如提供有效排序算法的vector-algorithms包在MVector
上进行排序。
但是,进一步的效率低下会造成比
所需的更多分配Data.Vector.Unboxed.fromList . take n . List.sortBy (flip $ Ord.comparing id)
$ [ ( sum [ (c + a) * wsum z | (z,c) <- IntMap.toList zt_d ] , d)
| d <- IntMap.keys $ m
, let zt_d = IntMap.findWithDefault IntMap.empty d $ m ]
写作时
d <- IntMap.keys m, let zt_d = IntMap.findWithDefault IntMap.empty d m
-- The '$' are unnecessary, I left them out
你是1)遍历整个地图以收集密钥列表,然后2)然后自己查找每个密钥。由于您只查找地图中存在的键,因此您永远不会使用默认值。更有效的是在地图的一次遍历中创建键/值对列表:
(d,zt_d) <- IntMap.assocs m
然后如果id
中的flip $ Ord.comparing id
确实是身份函数,那么sortBy (flip compare)
会更具可读性(并且可能更有效)。
根据求和元素的类型(可能还有优化级别),最好使用Data.List.foldl' (+) 0
代替sum
。