Performance 为什么Haskell数组填充操作如此缓慢?

Performance 为什么Haskell数组填充操作如此缓慢?,performance,haskell,Performance,Haskell,对于特定的任务,我需要在可变数组中进行大量快速、独立的写入。为了检查性能,我使用了以下测试: size :: Int size = 256*256*16 arr :: UArray Int Int arr = runST $ do arr <- newArray (0,size) 0 :: ST s (STUArray s Int Int) forM_ [0..size] $ \i -> do writeArray arr i i unsa

对于特定的任务,我需要在可变数组中进行大量快速、独立的写入。为了检查性能,我使用了以下测试:

size :: Int
size = 256*256*16

arr :: UArray Int Int
arr = runST $ do
    arr <- newArray (0,size) 0 :: ST s (STUArray s Int Int)
    forM_ [0..size] $ \i -> do
        writeArray arr i i 
    unsafeFreeze arr

arr_sum = foldl' (\ sum i -> sum + (arr ! i)) 0 [0..size-1]

main = print arr_sum
我怀疑在内存中填充256*256*16数组不需要0.7秒,所以我用JavaScript测试了一个等效程序:

size = 256*256*16;
x = new Array(size);
s = 0;
for (var i=0; i<size; ++i)
    x[i] = i;
for (var i=0; i<size; ++i)
    s += x[i];
console.log(s);
在C上,时间为
0.012s
,这是一个很好的下限

#include <stdio.h>

#define SIZE (256*256*16)
double x[SIZE];

int main(){
    int i;
    double s = 0;
    for (i = 0; i<SIZE; ++i)
        x[i] = i;
    for (i = 0; i<SIZE; ++i)
        s += x[i];
    printf("%f",s);
};
#包括
#定义大小(256*256*16)
双x[尺寸];
int main(){
int i;
双s=0;

对于(i=0;i<p>GHC)不会产生良好的紧密循环,就像你用C所完成的那样。在运行时间中,3的因子是根据我的经验来计算的。 要获得更好的性能,请使用向量库:

import qualified Data.Vector.Unboxed as V

size = 256*256*16 :: Int

doit = V.foldl' (+) 0 vec
  where vec = V.generate size id 

main = print doit

这对于评论来说太大了,但不是真正的答案。您的导入有点烦人,难以追踪,而且我还消除了
-Wall
中的警告(在查看性能时需要注意):

对于GHC 7.8和7.6,我得到了基本相同的计时(我必须
导入Data.Array.ST hiding(unsafeFreeze)
,但其他代码是相同的)

编辑:哎呀,看看我不是很善于观察;请注意,在我的32位机器上,计数在haskell中溢出,但在JS中没有溢出,因此我们有另一个苹果对桔子的比较;更公平的比较可能是
整数

我绝对推荐使用Criteria进行任何类型的微观基准测试,否则会浪费大量时间


另外,我认为在
C
版本中初始化数组的开销不大,所以这不是一个公平的比较。

对不起,我想只有我能正确回答这个问题。对于任何好奇的人来说,原因与代码无关,但事实上GHC没有用
-O2当我对它们进行基准测试时。解决方案是使用
force rrecomp
标志:

ghc -fforce-recomp -O2 bench.hs -o bench

#haskell@freenode上的人们建议的一个更好的解决方案是正确地设置阴谋集团,并使用它进行构建。

您使用的是什么版本的GHC?约塞米蒂岛上的7.8.4…对我来说,haskell版本是12毫秒(使用标准),而C版本是4.2毫秒(使用timespec from).你的机器和编译选项是什么?你没有对代码做任何更改吗?@AndrásKovács你可以在回答中提出这种风格:)为什么会有开销呢?为什么没有一个函数可以在没有开销的情况下进行累加计算呢?Integer太过分了…可能只是一个双精度函数,因为这是JavaScript使用的类型。很抱歉导入…我自己在处理数组代码时写的。一个好的API有多大的区别:)请记住在
foldl1'
中,还为未绑定向量定义了:
V.foldl1'(+)vec
…您还可以
触摸bench.hs;ghc-O2 bench.hs-o bench
。如果您正在编译多个模块,这将允许您重新编译所需的模块。当我想执行类似
-ddump simple
的操作时,我通常会这样做。
import qualified Data.Vector.Unboxed as V

size = 256*256*16 :: Int

doit = V.foldl' (+) 0 vec
  where vec = V.generate size id 

main = print doit
module Main where

import Data.Array.Unboxed
import Data.Array.ST
import Data.Array.Unsafe
import Control.Monad.ST
import Control.Monad
import Data.List

size :: Int
size = 256*256*16

ar :: UArray Int Int
ar = runST $ do
    a <- newArray (0,size) 0 :: ST s (STUArray s Int Int)
    forM_ [0..size] $ \i -> do
        writeArray a i i 
    unsafeFreeze a

arrSum :: Int
arrSum = foldl' (\ s i -> s + (ar ! i)) 0 [0..size-1]

main :: IO ()
main = print arrSum
jberryman /tmp » time ./t         
-524288
./t  0.04s user 0.01s system 92% cpu 0.056 total
jberryman /tmp » time nodejs t.js 
549755289600
nodejs t.js  0.19s user 0.01s system 100% cpu 0.200 total
ghc -fforce-recomp -O2 bench.hs -o bench