在 F# 中,有多少额外内存采用代数类型编码的值?
How much extra memory take a value encoded in a algebraic type in F#?
我正在为一种小语言做一个解释器来做 Array/relational 编程,就像在 kdb+ 中一样。
我想知道当一个值像这样在 AGDT 中编码时,F# 会增加多少内存:
type value =
| Dec of int
| Num of int array
| Float of float array
| Array of value
| Arr of value array
let print x =
printfn "%A" x
let a = [|1; 2|]
let b = Num(a)
let c = [| Dec(1); Dec(2) |]
//print (sizeof Arr) Don't have a easy way to do this
我想知道在 F# 中 a、b、c 的性能是否相同。这个想法是解释器需要主要处理数组。
因为看起来(根据我在 SO 中的搜索).NET 没有直接的方法来检查值的内存大小,所以我在 swift:
中做了类似的事情
indirect enum ExprC {
case IntC(x:Int32)
case IntA(x:Array<Int32>)
case ArrayC(x:Array<ExprC>)
}
let values:[Int32] = [1, 2]
let v1 = ExprC.ArrayC(x: [ExprC.IntC(x:1), ExprC.IntC(x: 2)])
let v2 = ExprC.IntA(x: values)
print(sizeofValue(values))
print(sizeofValue(v1))
print(sizeofValue(v2))
//RESULTS
//8
//8
//8
顺便说一句,这个结果出乎我的意料,我想编码一定有额外的存储成本,所以我不确定这是否会发生在 F# 中。
F# 编译器将 ADT 转换为 .NET 密封的 class 层次结构。你的例子将被翻译成这样的东西(粗略地说):
public class value {
public class Dec : value { public int Item { get; } }
public class Num : value { public int[] Item { get; } }
public class Float : value { public double[] Item { get; } }
public class Array : value { public value Item { get; } }
public class Arr : value { public value[] Item { get; } }
}
除此之外,真的很难说,因为 classes 的分配方式可能因 VM 实现和底层机器架构而异。例如,在 Windows 和 x86/x64 上的完整 .NET FW 上,object 将具有 32 位或 64 位 object header 加上任何object 的内容可能是对齐的(查看 this post 了解一些细节)。这意味着,例如,您的 Dec
案例将占用 8 或 12 个字节。
但是,其他 VM 实现可能会做不同的事情。 CoreCLR、Mono、MicroFW、.NET CF - 所有人都可能对 object 分配有自己的看法,.NET Native 编译器甚至可能完全优化整个事情。
这就是为什么 .NET 无法告诉您 class 的 "size":一般来说,它不知道。想一想,谈论 "size" 甚至可能并不总是有意义的。 Swift 可以摆脱它,因为它没有 open-standard 虚拟机,所以它可以为所欲为。
如果你真的需要保证特定的内存布局,.NET 确实有一个东西(查找 StructLayoutAttribute),但它不适用于 ADT,这是可以理解的。
最后,我觉得你的注意力可能被误导了。为什么你想知道这些事情,究竟是什么?如果你真的关心内存占用,你根本不应该使用 F#(或 Swift),你应该使用 C(或者,如果你真的喜欢函数式编程,请查看 Rust)。否则,请记住 "premature optimization".
我正在为一种小语言做一个解释器来做 Array/relational 编程,就像在 kdb+ 中一样。
我想知道当一个值像这样在 AGDT 中编码时,F# 会增加多少内存:
type value =
| Dec of int
| Num of int array
| Float of float array
| Array of value
| Arr of value array
let print x =
printfn "%A" x
let a = [|1; 2|]
let b = Num(a)
let c = [| Dec(1); Dec(2) |]
//print (sizeof Arr) Don't have a easy way to do this
我想知道在 F# 中 a、b、c 的性能是否相同。这个想法是解释器需要主要处理数组。
因为看起来(根据我在 SO 中的搜索).NET 没有直接的方法来检查值的内存大小,所以我在 swift:
中做了类似的事情indirect enum ExprC {
case IntC(x:Int32)
case IntA(x:Array<Int32>)
case ArrayC(x:Array<ExprC>)
}
let values:[Int32] = [1, 2]
let v1 = ExprC.ArrayC(x: [ExprC.IntC(x:1), ExprC.IntC(x: 2)])
let v2 = ExprC.IntA(x: values)
print(sizeofValue(values))
print(sizeofValue(v1))
print(sizeofValue(v2))
//RESULTS
//8
//8
//8
顺便说一句,这个结果出乎我的意料,我想编码一定有额外的存储成本,所以我不确定这是否会发生在 F# 中。
F# 编译器将 ADT 转换为 .NET 密封的 class 层次结构。你的例子将被翻译成这样的东西(粗略地说):
public class value {
public class Dec : value { public int Item { get; } }
public class Num : value { public int[] Item { get; } }
public class Float : value { public double[] Item { get; } }
public class Array : value { public value Item { get; } }
public class Arr : value { public value[] Item { get; } }
}
除此之外,真的很难说,因为 classes 的分配方式可能因 VM 实现和底层机器架构而异。例如,在 Windows 和 x86/x64 上的完整 .NET FW 上,object 将具有 32 位或 64 位 object header 加上任何object 的内容可能是对齐的(查看 this post 了解一些细节)。这意味着,例如,您的 Dec
案例将占用 8 或 12 个字节。
但是,其他 VM 实现可能会做不同的事情。 CoreCLR、Mono、MicroFW、.NET CF - 所有人都可能对 object 分配有自己的看法,.NET Native 编译器甚至可能完全优化整个事情。
这就是为什么 .NET 无法告诉您 class 的 "size":一般来说,它不知道。想一想,谈论 "size" 甚至可能并不总是有意义的。 Swift 可以摆脱它,因为它没有 open-standard 虚拟机,所以它可以为所欲为。
如果你真的需要保证特定的内存布局,.NET 确实有一个东西(查找 StructLayoutAttribute),但它不适用于 ADT,这是可以理解的。
最后,我觉得你的注意力可能被误导了。为什么你想知道这些事情,究竟是什么?如果你真的关心内存占用,你根本不应该使用 F#(或 Swift),你应该使用 C(或者,如果你真的喜欢函数式编程,请查看 Rust)。否则,请记住 "premature optimization".