F#中代数类型编码的值占用了多少额外内存



我正在为一种小语言做一个解释器,用于像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密封的类层次结构。你的例子会被翻译成这样(粗略地说):
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; } }
}

除此之外,这真的很难说,因为类的分配方式可能会随着VM实现和底层机器体系结构的不同而不同。例如,在x86/x64上的Windows上的完整.NET FW上,对象将有一个32或64位的对象标头,加上对象的任何内容,可能是对齐的(查看本文了解一些详细信息)。这意味着,例如,您的Dec大小写将占用8或12个字节。

然而,其他VM实现可能会做不同的事情。CoreCLR、Mono、MicroFW、.NET CF——所有这些都可能对对象分配有自己的看法,而.NET Native编译器甚至可能完全优化整个过程。

这就是为什么.NET一般不能告诉你类的"大小":一般来说,它不知道。仔细想想,谈论"尺寸"可能并不总是有意义的。Swift可以逃脱惩罚,因为它没有一个开放的标准虚拟机,所以它可以随心所欲。

如果你真的需要保证一个特定的内存布局,.NET确实有这样的功能(查找StructLayoutAttribute),但它不适用于ADT,这是可以理解的。

最后,我觉得你可能在误导你的注意力。你为什么想知道这些事情?如果你真的很关心内存占用,你根本不应该使用F#(或Swift),你应该使用C(或者,如果你真喜欢函数式编程,请查看Rust)。否则,请记住"过早优化"。

相关内容

  • 没有找到相关文章

最新更新