对于密集访问,先冻结数组是好是坏?
For dense access, is it better or worse to freeze an array first?
假设我有一个 Data.Array.IO.IOArray i e
(来自 array
包),我想从中读取元素,根据一些索引在 IO 中一个一个地处理每个元素订购:
arr :: IOArray I E
ordering :: [I]
processElement :: I -> E -> IO ()
(正如我们所见,一切都是单态的)。
有两种明显的方法可以做到这一点:
freeze
首先将数组转换为不可变数组,然后使用 !
运算符:
do
arr' <- freeze arr
forM_ ordering $ \i -> processElement i (arr' ! i)
readArray
'ing 来自原始可变数组的每个元素:
forM_ ordering $ \i -> do
e <- readArray arr i
processElement i e
ordering
中的索引是“密集”的,因为 arr
中的大多数索引都出现在 ordering
中。
哪个更有效率?另外,答案是否取决于这些因素:
- 索引
ordering
是否有重复索引?
- 指标
ordering
是否单调递增?
- 索引
ordering
是完全随机的还是具有较大的连续延伸?
没关系。从可变数组中读取与从不可变数组中读取相同,除非在实现中存在明显的问题,例如其中一个版本 inlines/unpacks 而另一个版本没有。
如果您希望写入数组,则不必为了索引而冻结。如果您不希望写入数组,冻结可能是有益的,因为它会使数组脱离 mutable list,从而降低 GC 开销。然而,由于竞技场规模很大且可变阵列数量很少,
即使这样也无所谓。
关于读取访问的模式,适用一般的位置注意事项。越密集越好,另外,如果可能的话,我们应该避免使用更大的 2 次方步幅进行迭代,以减少缓存冲突。
假设我有一个 Data.Array.IO.IOArray i e
(来自 array
包),我想从中读取元素,根据一些索引在 IO 中一个一个地处理每个元素订购:
arr :: IOArray I E
ordering :: [I]
processElement :: I -> E -> IO ()
(正如我们所见,一切都是单态的)。
有两种明显的方法可以做到这一点:
freeze
首先将数组转换为不可变数组,然后使用!
运算符:do arr' <- freeze arr forM_ ordering $ \i -> processElement i (arr' ! i)
readArray
'ing 来自原始可变数组的每个元素:forM_ ordering $ \i -> do e <- readArray arr i processElement i e
ordering
中的索引是“密集”的,因为 arr
中的大多数索引都出现在 ordering
中。
哪个更有效率?另外,答案是否取决于这些因素:
- 索引
ordering
是否有重复索引? - 指标
ordering
是否单调递增? - 索引
ordering
是完全随机的还是具有较大的连续延伸?
没关系。从可变数组中读取与从不可变数组中读取相同,除非在实现中存在明显的问题,例如其中一个版本 inlines/unpacks 而另一个版本没有。
如果您希望写入数组,则不必为了索引而冻结。如果您不希望写入数组,冻结可能是有益的,因为它会使数组脱离 mutable list,从而降低 GC 开销。然而,由于竞技场规模很大且可变阵列数量很少, 即使这样也无所谓。
关于读取访问的模式,适用一般的位置注意事项。越密集越好,另外,如果可能的话,我们应该避免使用更大的 2 次方步幅进行迭代,以减少缓存冲突。