减少分配排序大列表(或向量)

时间:2012-03-02 16:35:08

标签: sorting haskell allocation

我正在尝试减少程序中的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

1 个答案:

答案 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