Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance IOUArray和STUArray阵列之间的性能差异(在主筛中)_Performance_Haskell - Fatal编程技术网

Performance IOUArray和STUArray阵列之间的性能差异(在主筛中)

Performance IOUArray和STUArray阵列之间的性能差异(在主筛中),performance,haskell,Performance,Haskell,我使用可变数组在Haskell中制作了一个素筛。两个主要的可变数组是IOUArray和STUArray,因此我检查了这两个数组的性能 在我的计算机上,IOUArray的运行速度是STUArray的五倍,尽管代码结构基本相同。这是预期的吗?我是否缺少一些东西可以让STUArray运行得更快 编辑:我做了一些分析,结果可以在代码下面找到 代码如下: {-# LANGUAGE FlexibleContexts #-} module Main where import Data.Array.MArra

我使用可变数组在Haskell中制作了一个素筛。两个主要的可变数组是
IOUArray
STUArray
,因此我检查了这两个数组的性能

在我的计算机上,
IOUArray
的运行速度是
STUArray
的五倍,尽管代码结构基本相同。这是预期的吗?我是否缺少一些东西可以让
STUArray
运行得更快

编辑:我做了一些分析,结果可以在代码下面找到

代码如下:

{-# LANGUAGE FlexibleContexts #-}
module Main where

import Data.Array.MArray
import Data.Array.IO
import Data.Array.ST
import Control.Monad.ST
import Data.Array.Unboxed
import Control.Monad
import System.IO
import System.Environment (getArgs)

main :: IO ()
main = mainIO 
--main = mainST

mainST :: IO ()
mainST = do
  [n',outdir] <- getArgs
  let n      = read n' :: Int
      primes = primeSieveST n
  writeFile outdir (unlines . map show $ primes)
  putStrLn "Primes found using STUArray"

mainIO :: IO ()
mainIO = do
  [n',outdir] <- getArgs
  let n      = read n' :: Int
  primes <- primeSieveIO n
  writeFile outdir (unlines . map show $ primes)
  putStrLn "Primes found using IOUArray"

-- Prime sieve using IOUArray
primeSieveIO :: Int -> IO [Int]
primeSieveIO n = do
  arr <- newArray (1,n) True :: IO (IOUArray Int Bool)
  writeArray arr 1 False
  let p=2
  forM_ [p..n] $ \a -> do
      v <- readArray arr a
      if v then markOff arr a n
           else return ()
  iarr <- freeze arr :: IO (UArray Int Bool)
  return . map fst . filter (\(_,a)-> a) $ assocs iarr

-- Prime sieve using STUArray
primeSieveST :: Int -> [Int]
primeSieveST n = map fst . filter (\(_,a) -> a) . assocs $ runSTUArray $ do
  arr <- newArray (1,n) True
  writeArray arr 1 False
  let p = 2
  forM_ [p..n] $ \a -> do
      v <- readArray arr a
      if v then markOff arr a n 
           else return ()
  return arr


markOff :: (Integral i,Ix i, MArray a Bool m)  => a i Bool -> i -> i -> m ()
markOff arr a n = do
  forM_ [2*a,2*a+a..n] $ \b -> writeArray arr b False 
使用
STUArray

COST CENTRE    MODULE SRC                          %time %alloc

markOff        Main   app/Main.hs:(60,1)-(61,53)    64.1   55.5
primeSieveIO   Main   app/Main.hs:(35,1)-(44,54)    25.2   34.7
mainIO         Main   app/Main.hs:(26,1)-(31,40)     5.9    9.8
markOff.\      Main   app/Main.hs:61:32-53           3.6    0.0
primeSieveIO.\ Main   app/Main.hs:(39,24)-(42,33)    1.3    0.0
COST CENTRE  MODULE    SRC                         %time %alloc

markOff.\    Main      app/Main.hs:61:32-53         63.1   51.9
markOff      Main      app/Main.hs:(60,1)-(61,53)   26.7   33.7
primeSieveST Main      app/Main.hs:(48,1)-(56,12)    7.5   10.9
mainST       Main      app/Main.hs:(18,1)-(23,40)    1.8    2.9

因此,出于某种原因,数组的写入函数花费的时间要长得多。该程序是使用OSX上的堆栈构建和运行的。第61:32-53行上的函数是
writeArray
函数。

我不确定是什么影响了您的测量值,但将
main
替换为以下内容

import Criterion
import Criterion.Main

main = do
  n <- readLn
  defaultMain
    [ bench "io"  $ nfIO (primeSieveIO n)
    , bench "st"  $ nf primeSieveST n
    ]

当我考虑文件写入时,
IO
版本要慢得多。您是在编译此文件还是在GHCi中运行?如果是后者,请尝试编译。

已编辑,以添加更多细节和更好的解决方法

我可以用堆栈lts-8.5复制这个问题,用
-O3
编译

在查看生成的GHC内核之后,看起来在IO情况下,编译器能够生成一个专门版本的
markOff
,该版本将数组写入(字面上是将位数组中的位设置内联)内联到循环中。对于ST的情况,它使用了一个通用版本的
markOff
,并且每次写入都要通过
unsafeWrite
多态调用,该调用在确切的
MArray
类型上调度,速度要慢得多

如果您添加名为
markOffST
markOffST
副本,且签名专用于
STUArray
s:

markOffST :: (Integral i,Ix i) => STUArray s i Bool -> i -> i -> ST s ()
markOffST arr a n = do
  forM_ [2*a,2*a+a..n] $ \b -> writeArray arr b False
然后在
primesivest
中使用它,编译器通过内联写入为
markOffST
生成上述专门化,您会发现ST版本与IO版本一样快(至少在使用
-O3
编译时是如此)

很难说这是不是编译器的“bug”。GHC只是在没有一点帮助的情况下,没有在ST案例中生成专门版本的
markOff

在不更改签名的情况下,一种解决方法是要求GHC内联
markOff
函数:

{-# INLINE markOff #-}
markOff :: ...

它允许在
primesiveio
primesiveest
中生成专门的循环代码,就我所知,ST monad最终是不可变的计算。在这种情况下,这是很自然的。
ST
版本肯定会更快,因为
runSTUArray
在内部使用,而您的
IO
版本(不必要)使用复制版本。顺便说一句,GHC中正在进行的一些工作可能会导致
ST
操作总体上得到更好的优化,而一些
IO
操作会稍微慢一点(以获得更好的行为)。所以请坚持使用
ST
。嗯。。。
nf
nfIO
是否评估整个
[Int]
列表?这不只是比较第一个元素需要多少时间吗?(如果是这样的话,这真的有关系吗?@chi
nf
代表正常形式(与
wnhf
相反),所以我希望这是针对整个列表的。@Alec,我正在用堆栈编译和执行。我只是做了一些分析,结果被添加到我原来的帖子中。稍后我将尝试
标准
基准测试。是的,这似乎有效。这是ghc中的错误吗?当类型签名太一般时,它无法正确优化。我在回答中添加了更多细节。我不确定这本身就是一个bug。优化版的
markOff
只能针对比一般签名更特殊的类型签名生成;只是对于IO的情况,GHC会继续生成一个额外的、专门的定义,它不会为ST版本生成这个定义。
{-# INLINE markOff #-}
markOff :: ...