Haskell实时更新和查找性能

时间:2011-11-20 10:25:58

标签: arrays performance haskell profiling mutable

我正在写一个游戏ai(aichallenge.org - Ants),这需要对数据结构进行大量更新和引用。我已经尝试了数组和地图,但基本问题似乎是每次更新都会创建一个新值,这会让它变慢。如果您花费超过一秒钟来进行移动,游戏会引导您,因此应用程序将被视为“硬实时”。是否有可能在Haskell中具有可变数据结构的性能,或者我应该学习Python,还是在OCaml中重写我的代码?

我完全重写了蚂蚁“starter-pack”。从阵列更改为地图,因为我的测试显示地图更新速度更快。

我运行了地图版本并进行了性能分析,结果显示仅有20%的时间是由地图更新完成的。

这是一个简单的演示,说明阵列更新的速度有多慢。

slow_array =
    let arr = listArray (0,9999) (repeat 0)
        upd i ar = ar // [(i,i)]
    in  foldr upd arr [0..9999]

现在评估slow_array!9999需要将近10秒!虽然一次应用所有更新会更快,但该示例模拟了每个回合必须更新数组的真正问题,最好是每次在计划下一轮时选择移动。


感谢nponeccop和Tener参考矢量模块。以下代码等同于我的原始示例,但运行时间为0.06秒而不是10秒。

import qualified Data.Vector.Unboxed.Mutable as V

fast_vector :: IO (V.IOVector Int)
fast_vector = do
  vec <- V.new 10000
  V.set vec 0
  mapM_ (\i -> V.write vec i i) [0..9999]
  return vec

fv_read :: IO Int
fv_read  = do
  v <- fast_vector
  V.read v 9999

现在,将其纳入我的蚂蚁代码......

3 个答案:

答案 0 :(得分:12)

首先,想想你是否可以改进你的算法。另请注意,默认Ants.hs不是最佳的,您需要自己动手。

其次,您应该使用profiler来查找性能问题,而不是依赖于挥手。 Haskell代码通常比Python快得多(快10到30倍,你可以查看Language Shootout进行比较),即使是功能数据结构,所以你可能做错了。

Haskell supports mutable data非常好。请参阅ST(状态线程)和库ST的可变数组。另请查看vectors包。最后,您可以使用数据并行haskell,haskell-mpi或other ways of parallelization来加载所有可用的CPU内核,甚至可以在多台计算机上分配工作。

您使用的是已编译的代码(例如cabal buildghc --make)还是使用runhaskellghci?后者是字节码解释器,并且创建比本机代码编译器慢得多的代码。请参阅Cabal reference - 这是构建应用程序的首选方法。

还要确保您已开启优化(-O2other flags)。请注意,-O vs -O2可能有所不同,并尝试使用不同的后端,包括新的LLVM后端(-fllvm)。

答案 1 :(得分:4)

一次更新数组一个元素是非常低效的,因为每次更新都涉及制作整个数组的副本。其他数据结构(如Map)实现为树,因此允许对数时间更新。但是,通常一次更新功能数据结构一个元素通常是次优的,所以你应该尝试退一步思考如何实现某些东西作为整个结构的转换,而不是单个元素一次。

例如,通过一步完成所有更新,只需要复制一次数组就可以更有效地编写slow_array示例。

faster_array =
    let arr = listArray (0,9999) (repeat 0)
    in  arr // [(i,i) | i <- [0..9999]]

如果你不能想到一个一元一元的算法的替代方案,那么可变数据结构已经被提到作为另一种选择。

答案 2 :(得分:2)

您基本上要求可变数据结构。除了标准库,我建议你查找:

那就是说,我不太确定你需要它们。对于持久数据结构也有简洁的算法。 Data.Map的快速替换是此包中的哈希表: