我正在写一个游戏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
现在,将其纳入我的蚂蚁代码......
答案 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 build
或ghc --make
)还是使用runhaskell
或ghci
?后者是字节码解释器,并且创建比本机代码编译器慢得多的代码。请参阅Cabal reference - 这是构建应用程序的首选方法。
还要确保您已开启优化(-O2
和other 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的快速替换是此包中的哈希表: