Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.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 Haskell向量性能与Scala的比较_Performance_Scala_Haskell - Fatal编程技术网

Performance Haskell向量性能与Scala的比较

Performance Haskell向量性能与Scala的比较,performance,scala,haskell,Performance,Scala,Haskell,我在Haskell和Scala中有一段非常简单的代码。此代码旨在以非常紧密的循环运行,因此性能至关重要。问题是Haskell比Scala慢10倍左右。这里是Haskell代码 {-# LANGUAGE BangPatterns #-} import qualified Data.Vector.Unboxed as VU newtype AffineTransform = AffineTransform {get :: (VU.Vector Double)} deriving (Show) {

我在Haskell和Scala中有一段非常简单的代码。此代码旨在以非常紧密的循环运行,因此性能至关重要。问题是Haskell比Scala慢10倍左右。这里是Haskell代码

{-# LANGUAGE BangPatterns #-}
import qualified Data.Vector.Unboxed as VU

newtype AffineTransform = AffineTransform {get :: (VU.Vector Double)} deriving (Show)

{-# INLINE runAffineTransform #-}
runAffineTransform :: AffineTransform -> (Double, Double) -> (Double, Double)
runAffineTransform affTr (!x, !y) = (get affTr `VU.unsafeIndex` 0 * x + get affTr `VU.unsafeIndex` 1 * y + get affTr `VU.unsafeIndex` 2, 
                                      get affTr `VU.unsafeIndex` 3 * x + get affTr `VU.unsafeIndex` 4 * y + get affTr `VU.unsafeIndex` 5)

testAffineTransformSpeed :: AffineTransform -> Int -> (Double, Double)
testAffineTransformSpeed affTr count = go count (0.5, 0.5)
  where go :: Int -> (Double, Double) -> (Double, Double)
        go 0 res = res
        go !n !res = go (n-1) (runAffineTransform affTr res)

还可以做些什么来改进此代码?

我定义了以下严格/非固定对类型:

import System.Random.MWC -- for later
import Control.DeepSeq

data SP = SP {
    one :: {-# UNPACK #-} !Double
  , two :: {-# UNPACK #-} !Double
  } deriving Show

instance NFData SP where
  rnf p = rnf (one p) `seq` rnf (two p) `seq` ()
并在
runAffineTransform
函数中替换它:

runAffineTransform2 :: AffineTransform -> SP -> SP
runAffineTransform2 affTr !(SP x y) =
  SP (  get affTr `U.unsafeIndex` 0 * x
      + get affTr `U.unsafeIndex` 1 * y
      + get affTr `U.unsafeIndex` 2     )

     (  get affTr `U.unsafeIndex` 3 * x
      + get affTr `U.unsafeIndex` 4 * y
      + get affTr `U.unsafeIndex` 5     )
{-# INLINE runAffineTransform2 #-}
然后运行这个基准测试套件:

main :: IO ()
main = do
  g  <- create
  zs <- fmap (AffineTransform . U.fromList)
             (replicateM 100000 (uniformR (0 :: Double, 1) g))

  let myConfig = defaultConfig { cfgPerformGC = ljust True }

  defaultMainWith myConfig (return ()) [
      bench "yours" $ nf (testAffineTransformSpeed  zs) 10
    , bench "mine"  $ nf (testAffineTransformSpeed2 zs) 10
    ]
完整的代码在要点中

编辑


我还发布了Criteria的输出报告。

主要问题是

runAffineTransform affTr (!x, !y) = (get affTr `VU.unsafeIndex` 0 * x
                                     + get affTr `VU.unsafeIndex` 1 * y
                                     + get affTr `VU.unsafeIndex` 2, 
                                       get affTr `VU.unsafeIndex` 3 * x
                                     + get affTr `VU.unsafeIndex` 4 * y
                                     + get affTr `VU.unsafeIndex` 5)
产生一对重击。调用
runAffineTransform
时,不会对组件进行评估,直到某些消费者要求对组件进行评估时,组件才会停止运行

testAffineTransformSpeed affTr count = go count (0.5, 0.5)
  where go :: Int -> (Double, Double) -> (Double, Double)
        go 0 res = res
        go !n !res = go (n-1) (runAffineTransform affTr res)
不是那个消费者,bang-on
res
只对最外层的构造函数求值,
(,)
,然后得到

runAffineTransform affTr (runAffineTrasform affTr (runAffineTransform affTr (...)))
只有在最后需要范式时才对其求值

如果强制立即计算结果的组件

runAffineTransform affTr (!x, !y) = case
  (  get affTr `U.unsafeIndex` 0 * x
   + get affTr `U.unsafeIndex` 1 * y
   + get affTr `U.unsafeIndex` 2
  ,  get affTr `U.unsafeIndex` 3 * x
   + get affTr `U.unsafeIndex` 4 * y
   + get affTr `U.unsafeIndex` 5
  ) of (!a,!b) -> (a,b)
让它内联,使用自定义严格的unbox
Double
s对的版本的主要区别在于,对于
testAffinetTransformsSpeed
中的循环,使用box
Double
s作为参数得到一个初始迭代,最后,结果的组件被装箱,这增加了一点恒定的开销(我的盒子上每个循环大约5纳秒)。在这两种情况下,循环的主要部分都使用一个
Int 35;
和两个
Double#
参数,循环体是相同的,除了到达
n=0
时的装箱


当然,使用未绑定的严格对类型强制立即计算组件更好。

Nice,现在在我的计算机上,它的性能比Scala(Java)稍好一些。我们应该吸取什么教训?严格注释不够(它们不会自动取消装箱?)?有时您必须手动创建未打包(未打包)的数据结构?@user2705843幻灯片(4)和(9)在这里是相关的:
NFData
SP
实例完全是浪费工作。事实上,很多都是这样。它隐式创建
Double
构造函数,指向
SP
中的未打包值,强制它们,然后丢弃它们
rnf a=seq a()
将更加高效和正确。@Carl谢谢。不过,在这里进行更改不会产生任何(时间)差异。@Carl你低估了GHC,
case p\u aIf of{AffTran.SP\uu->GHC.Tuple。}
是它为
NFData
实例生成的。它知道组件已解除绑定,因此没有理由再次对其进行评估。您是如何编译的,使用哪种编译器?它是使用ghc 7.6.3编译的。选项是“-O2-Wall-funbox严格字段-线程化-rtsopts”。我原以为-funbox严格字段就足够了,但事实并非如此。嗯,我是一个完全的新手,所以我的期望值可能有点低。为什么需要
Vector
s来表示仿射变换?为它制作一个ADT并处理坐标数组似乎更合理。您可以将仿射变换ADT作为
固定向量的一个实例,并获得向量运算的所有功能。你说的“独立坐标”是什么意思?最后,您的应用程序中是否有更多的坐标或变换?您的代码要复杂得多。对固定大小的结构使用数组在任何语言中都是不自然的,从
C
Haskell
!(!x,!y)
有用吗?在
中让
在哪里绑定。但在这里,它只是从
中删除了太少的一个字符的一个残余!res@(!x,!y)
。谢谢你的注意。
runAffineTransform affTr (!x, !y) = case
  (  get affTr `U.unsafeIndex` 0 * x
   + get affTr `U.unsafeIndex` 1 * y
   + get affTr `U.unsafeIndex` 2
  ,  get affTr `U.unsafeIndex` 3 * x
   + get affTr `U.unsafeIndex` 4 * y
   + get affTr `U.unsafeIndex` 5
  ) of (!a,!b) -> (a,b)