4

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)

このコードを改善するために、さらに何ができるでしょうか?

4

2 に答える 2

8

主な問題は、

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)

はその消費者ではなく、バングオンは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)

インライン化します。ボックス化されていない sのカスタム厳密なペアを使用するjtobinのバージョンとの主な違いDouble#は、ループの場合、ボックス化されたs を引数としてtestAffineTransformSpeed使用して最初の反復を 1 回取得し、最後に結果のコンポーネントを取得することです。Doubleボックス化されているため、一定のオーバーヘッドが少し追加されます (私のボックスでは、ループごとに約 5 ナノ秒です)。ループの主要部分は、どちらの場合もInt#1 つと 2 つの引数を取り、ループ本体はに到達Double#したときのボックス化を除いて同じです。n = 0

もちろん、ボックス化されていない厳密なペア型を使用して、コンポーネントの即時評価を強制する方が適切です。

于 2013-08-22T12:14:57.723 に答える