flipflop
flipflop

Reputation: 103

Understanding F# memory consumption

I've been toying around with F# lately and wrote this little snippet below, it just creates a number of randomized 3d-vectors, puts them into a list, maps each vector to its length and sums up all those values.

Running the program (as a Release Build .exe, not interactive), the binary consumes in this particular case (10 mio vectors) roughly 550 MB RAM. One Vec3 object should account for 12 bytes (or 16 assuming some alignment takes place). Even if you do the rough math with 32 bytes to account for some book-keeping overhead (bytes per object*10 mio) / 1024 / 1024) you're still 200 MB off the actual consumption. Naively i'd assume to have 10 mio * 4 bytes per single in the end, since the Vec3 objects are 'mapped away'.

My guess so far: either i keep one (or several) copy/copies of my list somewhere and i'm not aware of that, or some intermediate results get never garbage collected? I can't imagine that inheriting from System.Object brings in so much overhead. Could someone point me into the right direction with this?

TiA

type Vec3(x: single, y: single, z:single) = 

    let mag = sqrt(x*x + y*y + z*z)

    member self.Magnitude = mag

    override self.ToString() = sprintf "[%f %f %f]" x y z

let how_much = 10000000

let mutable rng = System.Random()

let sw = new System.Diagnostics.Stopwatch()
sw.Start()

let random_vec_iter len =

    let mutable result = []

    for x = 1 to len do
        let mutable accum = []

        for i = 1 to 3 do
            accum <- single(rng.NextDouble())::accum
        result <- Vec3(accum.[0], accum.[1], accum.[2])::result
    result

sum_len_func = List.reduce (fun x y -> x+y)
let map_to_mag_func = List.map (fun (x:Vec3) -> x.Magnitude)

[<EntryPoint>]
let main argv = 
    printfn "Hello, World"
    let res = sum_len_func (map_to_mag_func (random_vec_iter(how_much)))
    printfn "doing stuff with %i items took %i, result is %f" how_much     (sw.ElapsedMilliseconds) res
    System.Console.ReadKey() |> ignore
    0 // return an integer exit code

Upvotes: 3

Views: 1027

Answers (1)

Daniel Fabian
Daniel Fabian

Reputation: 3838

First, your vec is a ref type not a value type (not a struct). So you hold a pointer on top of your 12 bytes (12+16). Then the list is a single-linked list, so another 16 bytes for a .net ref. Then, your List.map will create an intermediate list.

Upvotes: 3

Related Questions