私は時系列で作業してtype TSeries = [(Day, Double)]
いますがDay
、さらに処理するために最初の要素をDoubleに変換する必要があります(プロットなど)。
日付範囲を対応する倍精度範囲[lobound、upbound]にマッピングします。ここで、最も早い日付はloboundにマップされ、最も遅い日付はupboundにマップされます。これは基本的な変換です。それを実装するには、最初に日付範囲の最小値と最大値を取得する必要があります。パフォーマンスの問題が発生しましたが、正確な理由と修正方法がわかりません。
コードは次のとおりです(時系列はソートされているとは想定されていません)。
module Main where
import Data.Time (Day, fromGregorian, diffDays)
type TSeries = [(Day, Double)]
-- time-series to (Double, Double) mapping function
toDbl :: (Day -> Double) -> TSeries -> [(Double, Double)]
toDbl mapX ts = map (\(d,x) -> (mapX d, x)) ts
-- Day to Double mapping function - fast
mapDays1 :: (Day, Double) -> (Day, Double) -> Day -> Double
mapDays1 (d0,x0) (d1,x1) d = ((fromIntegral $ diffDays d d0) * x1 + (fromIntegral $ diffDays d1 d) * x0) / diff10
where diff10 = fromIntegral $ diffDays d1 d0
-- Day to Double mapping function - slow
mapDays2 :: TSeries -> Double -> Double -> Day -> Double
mapDays2 ts x0 x1 d = mapDays1 (d0,x0) (d1,x1) d
where d0 = minimum $ map fst ts
d1 = maximum $ map fst ts
-- example time-series
func :: Int -> Double
func d = sin $ pi / 14 * (fromIntegral d)
ts = [(fromGregorian y m d, func d) | y <- [2000..2016], m <- [1..12], d <- [1..28]] :: TSeries
-- speed test
main = do
let mindate = minimum $ map fst ts
maxdate = maximum $ map fst ts
test1 = toDbl (mapDays1 (mindate,0.0) (maxdate,100.0)) ts
test2 = toDbl (mapDays2 ts 0.0 100.0) ts
-- print $ sum $ map fst test1 -- this is fast
print $ sum $ map fst test2 -- this is slow
私が実行するテスト(X軸、最初に要素を合計)は関係ありませんが、単純であり、パフォーマンスの問題をよく示しています。
基本的にmapDays1とmapDays2は同じですが、適切なスケーリングを取得するには、最小日付と最大日付を外部で計算してmapDays1に渡す必要がありますが、これはmapDays2内で「内部的に」行われます。
問題は、mapDays2がmapDays1バージョンと比較して非常に遅いことです。最小計算と最大計算が(1回ではなく)何度も呼び出されると思われますが、その理由がわかりません。また、mapDays2を修正してmapDays1と同様のパフォーマンスを得る方法がわかりません。
問題は確かにメモ化に関連しています。問題は、あなたが呼び出すことですmapDays1
し、mapDays2
それらのコールだけサンクを作成するので、すべての引数を渡すことはありません。
サンクは唯一の内側に完了し得ることを意味することにmap
別の呼び出しがするので、mapDays2
その結果について共有することはできませんd0 = minimum $ map fst ts
し、d1 = maximum $ map fst ts
及び最大値と最小値のget再評価されるたびに。一つはどこな状況を想像d0
してd1
、最後に依存しDay
、正しくないだろう、その場合には、引数、ない再評価することd0
とd1
するたびに。
対照的に、それは非常に明確でmindate = minimum $ map fst ts
あり、maxdate = maximum $ map fst ts
一度だけ計算する必要があります。
mapDays2
f x y = e
と同じふりをしたいのですがf x = \y -> e
、ボンネットの下ではありません。最後の引数を除いてすべて渡されたときにGHCがサンクを作成しないようにする必要があります。d
等号の上に移動するだけです。次に、あなたが返す関数は一度だけ計算しますd0
とd1
:
-- Day to Double mapping function - slow
mapDays2 :: TSeries -> Double -> Double -> Day -> Double
mapDays2 ts x0 x1 = \d -> mapDays1 (d0,x0) (d1,x1) d
where d0 = minimum $ map fst ts
d1 = maximum $ map fst ts
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加