💠

F#で関数のメモリ使用量の比較

2024/02/16に公開

.NETのGC.GetTotalMemoryを使うことで.NET上のメモリ使用量をバイト単位で参照できます。

open System
let memory = GC.GetTotalMemory false
// val memory: int64 = 94499104L

関数呼び出しの前後で、このメモリ使用量を比較することで関数のメモリ使用量を推測できます。

let before = GC.GetTotalMemory false
f () // 何かしらの関数の呼び出し
let after = GC.GetTotalMemory false
let memory = after - before
// val memory: int64 = バイト単位のメモリ使用量

メモリ使用量を調べる関数をパラメータとして受け取ってメモリ使用量を調べる関数を定義して使い回すことができます。

let measureMemoryUsage (f: unit -> unit) =
    let before = GC.GetTotalMemory false
    f ()
    let after = GC.GetTotalMemory false
    after - before

以下は、最大公約数(greatest common divisor)を計算する関数について、逆畳み込み(unfold)を使った計算の関数(gcd)とループを使った計算の関数(gcd_while)でメモリ使用量を比較するプログラムです。

open System

let gcd u v =
    Seq.unfold
        (fun (x, y) ->
            if y = 0 then
                None
            else
                Some((x, y), (y, x % y)))
        (u, v)
    |> Seq.last
    |> snd

let gcd_while u v =
    let mutable x = u
    let mutable y = v
    let mutable t = 0

    while x > 0 do
        if x < y then
            t <- x
            x <- y
            y <- t

        x <- x - y

    y

let measureMemoryUsage (f: unit -> unit) =
    let before = GC.GetTotalMemory(true)
    f ()
    let after = GC.GetTotalMemory(true)
    after - before

let function1 () = printfn "%d" (gcd 461952 116298)
let function2 () = printfn "%d" (gcd_while 461952 116298)

let memoryUsage1 = measureMemoryUsage function1
let memoryUsage2 = measureMemoryUsage function2

printfn "Function 1 used %d bytes" memoryUsage1
printfn "Function 2 used %d bytes" memoryUsage2
18
18
Function 1 used 1184 bytes
Function 2 used 152 bytes

逆畳み込み(unfold)を使った計算の方がメモリ使用量が大きいことがわかります。

Discussion