f
ある入力を受け入れて数値を生成する関数があると仮定します。関数内でf
、入力に従ってリストが作成され、その後、(たとえばを使用してfoldl' g
)縮小されて、最終的な出力番号が生成されます。g
結局、中間リストはリデュースされるので、中間リストを表現せずにリデュース機能を適用することは可能ですか。ここでの目標は、リストの保存(または、精度の低い単語を「保存」する場合は表現)に使用されるメモリを制限することです。
これを説明するために、この関数は中間リスト用のスペースをfoldPairProduct
取りO(N1 * N2)
ます(消費されるスペースは、式と遅延評価のために複雑になる可能性がありますが、比例またはそれより悪いと思います)。N1, N2
2つの入力リストのサイズは次のとおりです。
foldPairProduct :: (Num a, Ord a) => (a -> a -> a) -> [a] -> [a] -> a
foldPairProduct f xs ys = foldl1 f [ x*y | x <- xs, y <- ys]
ロジックの代替実装はfoldPairProduct'で、これはO(2 * 2)
スペースを取ります。
foldPairProduct' :: Num a => (Maybe a -> Maybe a -> Maybe a) -> [a] -> [a] -> Maybe a
foldPairProduct' _ _ [] = Nothing
foldPairProduct' _ [] _ = Nothing
foldPairProduct' f (x:xs) (y:ys) =
foldl1 f [Just $ x*y, foldPairProduct' f [x] ys, foldPairProduct' f xs [y],
foldPairProduct' f xs ys]
複数のリストを入力として受け入れることを除いてfoldCrossProduct
、実装が類似している場合、状況は悪化します。foldPairProduct
中間リストのスペースの複雑さ(命令型言語の意味では)はO(N1 * N2 * ...* Nk)
、です。ここk
で、はの長さです[[a]]
。
foldCrossProduct :: Num a => (a -> a -> a) -> [[a]] -> a
foldCrossProduct f xss = foldl1 f (crossProduct xss)
crossProduct :: Num a => [[a]] -> [a]
crossProduct [] = []
crossProduct (xs:[]) = xs
crossProduct (xs:xss) = [x * y | x <- xs, y <- crossProduct xss]
の実装アイデアに従った場合foldPairProduct'
、スペースの複雑さはk^2
、はるかにスペース効率が高くなります。私の質問は次のとおりです。
foldPairProduct'
私はリストのペアのために実装しました。ただし、任意の数のリストに実装するのは簡単ではないようです。Haskellを命令型言語と比較するつもりはありませんが、一定のスペースを使用する(つまり、言及された長さの中間リストを表現せずに)実装はありますか?たぶんモナドは助けになるでしょうが、私はそれにとても慣れていません。
コンパイラは実際にその魔法を実行しますか?つまり、リストが中間であり、削減されることに気づき、実際にスペース効率でリストを評価する方法を見つけ出します。結局のところ、それが遅延評価とコンパイラ最適化が設計されていると私が思ったものです。
コメントは大歓迎です。ありがとう。
アップデート1
パフォーマンステストでは、入力サイズの変化に基づいて、foldPairProduct
およびの「スペースの複雑さ」に関する分析と、 GCによってコピーされたバイト数の観察が確認されました。 foldCrossProduct
N1, N2, N3
パフォーマンステストは、foldPairProduct'
驚くべきことにN1 * N2
、またはさらに悪いスペース使用量を示した分析を否定します。これはおそらく、再帰呼び出しが非効率的に評価されているためです。結果は以下に添付されています(ghc設定はYurasが行ったものと同じです)。
アップデート2
コメントと回答から学んだように、いくつかのさらなる実験を更新しました。の場合、使用中のメモリfoldPairProduct
の合計は、DanielFischerが説明したスペースの複雑さと一致しています。
なぜならダニエルのアドバイスに従って、とを入れ替えるfoldCrossProduct
、ダニエルの複雑さの分析は私には理にかなっていますが、結果は線形のメモリ使用量を示していません。x <- xs
とy <- crossproduct ys
、実際に線形空間の複雑さが実現します。
の場合foldCrossProduct (max) [[1..100],[1..n], [1..1000]]
、n = 100、1000、10000、100000の場合、使用されるメモリは2、2、3、14MBです。
foldPairProduct [1..n] [1..10000]
n = 100
120,883,320 bytes allocated in the heap
56,867,728 bytes copied during GC
428,384 bytes maximum residency (50 sample(s))
98,664 bytes maximum slop
3 MB total memory in use (0 MB lost due to fragmentation)
n = 1000
1,200,999,280 bytes allocated in the heap
569,837,360 bytes copied during GC
428,384 bytes maximum residency (500 sample(s))
99,744 bytes maximum slop
3 MB total memory in use (0 MB lost due to fragmentation)
n = 10000
12,002,152,040 bytes allocated in the heap
5,699,468,024 bytes copied during GC
428,384 bytes maximum residency (5000 sample(s))
99,928 bytes maximum slop
3 MB total memory in use (0 MB lost due to fragmentation)
n = 100000
120,013,672,800 bytes allocated in the heap
56,997,625,608 bytes copied during GC
428,384 bytes maximum residency (50000 sample(s))
99,984 bytes maximum slop
3 MB total memory in use (0 MB lost due to fragmentation)
foldPairProduct [1..10000] [1..n]
n = 100
121,438,536 bytes allocated in the heap
55,920 bytes copied during GC
32,408 bytes maximum residency (1 sample(s))
19,856 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
n = 1000
1,201,511,296 bytes allocated in the heap
491,864 bytes copied during GC
68,392 bytes maximum residency (1 sample(s))
20,696 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
n = 10000
12,002,232,056 bytes allocated in the heap
5,712,004,584 bytes copied during GC
428,408 bytes maximum residency (5000 sample(s))
98,688 bytes maximum slop
3 MB total memory in use (0 MB lost due to fragmentation)
n = 100000
120,009,432,816 bytes allocated in the heap
81,694,557,064 bytes copied during GC
4,028,408 bytes maximum residency (10002 sample(s))
769,720 bytes maximum slop
14 MB total memory in use (0 MB lost due to fragmentation)
foldPairProduct [1..n] [1..n]
n = 100
1,284,024 bytes allocated in the heap
15,440 bytes copied during GC
32,336 bytes maximum residency (1 sample(s))
19,920 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
n = 1000
120,207,224 bytes allocated in the heap
114,848 bytes copied during GC
68,336 bytes maximum residency (1 sample(s))
24,832 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
n = 10000
12,001,432,024 bytes allocated in the heap
5,708,472,592 bytes copied during GC
428,336 bytes maximum residency (5000 sample(s))
99,960 bytes maximum slop
3 MB total memory in use (0 MB lost due to fragmentation)
n = 100000
1,200,013,672,824 bytes allocated in the heap
816,574,713,664 bytes copied during GC
4,028,336 bytes maximum residency (100002 sample(s))
770,264 bytes maximum slop
14 MB total memory in use (0 MB lost due to fragmentation)
foldCrossProduct(max)[[1..n]、[1..100]、[1..1000]]
n = 100
105,131,320 bytes allocated in the heap
38,697,432 bytes copied during GC
427,832 bytes maximum residency (34 sample(s))
209,312 bytes maximum slop
3 MB total memory in use (0 MB lost due to fragmentation)
n = 1000
1,041,254,480 bytes allocated in the heap
374,148,224 bytes copied during GC
427,832 bytes maximum residency (334 sample(s))
211,936 bytes maximum slop
3 MB total memory in use (0 MB lost due to fragmentation)
n = 10000
10,402,479,240 bytes allocated in the heap
3,728,429,728 bytes copied during GC
427,832 bytes maximum residency (3334 sample(s))
215,936 bytes maximum slop
3 MB total memory in use (0 MB lost due to fragmentation)
foldCrossProduct(max)[[1..100]、[1..n]、[1..1000]]
n = 100
105,131,344 bytes allocated in the heap
38,686,648 bytes copied during GC
431,408 bytes maximum residency (34 sample(s))
205,456 bytes maximum slop
3 MB total memory in use (0 MB lost due to fragmentation)
n = 1000
1,050,614,504 bytes allocated in the heap
412,084,688 bytes copied during GC
4,031,456 bytes maximum residency (53 sample(s))
1,403,976 bytes maximum slop
15 MB total memory in use (0 MB lost due to fragmentation)
n = 10000
quit after over 1362 MB total memory in use (0 MB lost due to fragmentation)
foldPairProduct'[1..n] [1..n]
n = 100
4,351,176 bytes allocated in the heap
59,432 bytes copied during GC
74,296 bytes maximum residency (1 sample(s))
21,320 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
n = 1000
527,009,960 bytes allocated in the heap
45,827,176 bytes copied during GC
211,680 bytes maximum residency (1 sample(s))
25,760 bytes maximum slop
2 MB total memory in use (0 MB lost due to fragmentation)