了解 F# 内存消耗
Understanding F# memory consumption
我最近一直在玩弄 F#,并在下面写了这个小片段,它只是创建了一些随机的 3d 向量,将它们放入列表中,将每个向量映射到它的长度并将所有这些值相加.
运行 程序(作为 Release Build .exe,非交互式),二进制文件在此特定情况下(10 个 mio 向量)大约消耗 550 MB RAM。一个 Vec3 对象应该占 12 个字节(或者 16 个,假设发生了一些对齐)。即使您使用 32 字节进行粗略计算以计算一些簿记开销(每个对象的字节数 * 10 mio)/ 1024 / 1024),您仍然比实际消耗少 200 MB。天真地我假设最后每个单个有 10 mio * 4 字节,因为 Vec3 对象是 'mapped away'.
到目前为止我的猜测:要么我在某个地方保留了一个(或多个)copy/copies 我的列表但我不知道,或者一些中间结果从未被垃圾收集?我无法想象从 System.Object 继承会带来如此多的开销。
有人能用这个给我指出正确的方向吗?
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
首先,你的 vec 是引用类型而不是值类型(不是结构)。因此,您在 12 个字节 (12+16) 的顶部持有一个指针。那么该列表是一个单链表,所以另外 16 个字节用于 .net 引用。然后,您的 List.map 将创建一个中间列表。
我最近一直在玩弄 F#,并在下面写了这个小片段,它只是创建了一些随机的 3d 向量,将它们放入列表中,将每个向量映射到它的长度并将所有这些值相加.
运行 程序(作为 Release Build .exe,非交互式),二进制文件在此特定情况下(10 个 mio 向量)大约消耗 550 MB RAM。一个 Vec3 对象应该占 12 个字节(或者 16 个,假设发生了一些对齐)。即使您使用 32 字节进行粗略计算以计算一些簿记开销(每个对象的字节数 * 10 mio)/ 1024 / 1024),您仍然比实际消耗少 200 MB。天真地我假设最后每个单个有 10 mio * 4 字节,因为 Vec3 对象是 'mapped away'.
到目前为止我的猜测:要么我在某个地方保留了一个(或多个)copy/copies 我的列表但我不知道,或者一些中间结果从未被垃圾收集?我无法想象从 System.Object 继承会带来如此多的开销。 有人能用这个给我指出正确的方向吗?
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
首先,你的 vec 是引用类型而不是值类型(不是结构)。因此,您在 12 个字节 (12+16) 的顶部持有一个指针。那么该列表是一个单链表,所以另外 16 个字节用于 .net 引用。然后,您的 List.map 将创建一个中间列表。