有谁知道答案和/或对此有意见?
由于元组通常不会很大,我认为使用结构比使用类更有意义。你说什么?
答案 0 :(得分:89)
为简单起见,Microsoft制作了所有元组类型引用类型。
我个人认为这是一个错误。具有超过4个字段的元组是非常不寻常的,无论如何应该用更有类型的替代(例如F#中的记录类型)替换,因此只有小元组才有实际意义。我自己的基准测试表明,最多512字节的无盒式元组仍然比盒装元组更快。
虽然内存效率是一个问题,但我认为主要问题是.NET垃圾收集器的开销。在.NET上分配和收集非常昂贵因为它的垃圾收集器没有经过大量优化(例如与JVM相比)。此外,默认的.NET GC(工作站)尚未并行化。因此,当所有内核争用共享垃圾收集器时,使用元组的并行程序会停止运行,从而破坏可伸缩性。这不仅是主要关注点,而且AFAIK在审查这个问题时完全被微软所忽视。
另一个问题是虚拟调度。引用类型支持子类型,因此,通常通过虚拟分派调用其成员。相反,值类型不支持子类型,因此成员调用完全明确,并且始终可以作为直接函数调用执行。虚拟调度在现代硬件上非常昂贵,因为CPU无法预测程序计数器的结束位置。 JVM竭尽全力优化虚拟调度,但.NET却没有。但是,.NET确实以值类型的形式提供了虚拟调度的转义。因此,将元组表示为值类型可以再次显着提高性能。例如,在2元组上调用GetHashCode
一百万次需要0.17s,但在等效结构上调用它只需要0.008s,即值类型比参考类型快20倍。
通常出现元组性能问题的真实情况是使用元组作为字典中的键。我实际上是通过跟踪Stack Overflow问题F# runs my algorithm slower than Python!中的一个链接来发现这个线程的,其中作者的F#程序结果比他的Python慢,因为他使用的是盒装元组。使用手写struct
类型手动取消装箱使他的F#程序比Python快几倍,速度更快。如果元组由值类型表示而不是以...开头的引用类型,则永远不会出现这些问题。
答案 1 :(得分:43)
原因很可能是因为只有较小的元组作为值类型才有意义,因为它们的内存占用量很小。较大的元组(即具有更多属性的元组)实际上会受到影响,因为它们会大于16个字节。
不是让一些元组成为值类型而其他元素是引用类型,并迫使开发人员知道哪些是我想象的,微软的人认为使它们都是引用类型更简单。
啊,怀疑确认了!请参阅Building Tuple:第一个主要决定是否 把元组作为参考 或价值类型。既然他们是 在你想要改变的任何时候都是不可变的 你必须要有一个元组的值 创造一个新的。如果它们是 引用类型,这意味着可以 如果你生成很多垃圾 正在改变元组中的元素 紧环。 F#元组是参考 类型,但有一种感觉 他们可以实现的团队 性能改善,如果两个,和 也许三个元素元组是 值类型而不是。有些团队认为 已经创建了内部元组 值而不是引用类型, 因为他们的情景非常好 对创建大量托管敏感 对象。他们发现使用了一个值 类型给了他们更好的表现。在 我们的元组初稿 规格,我们保持两个, 三元组和四元组元组 值类型,其余为 参考类型。然而,在一个 包括设计会议 其他语言的代表 决定这个“分裂” 因为设计会令人困惑 两者之间的语义略有不同 这两种类型。行为一致性 并且设计被确定为 优先级高于潜力 性能提升。基于此 输入,我们改变了设计 所有元组都是引用类型, 虽然我们要求F#团队做 一些表现调查看 如果它在使用时经历了加速 某些大小的元组的值类型。 从那以后,它有一个很好的方法来测试它 它的编译器,用F#编写,是一个 一个大型程序的好例子 在各种场景中使用元组。 最后,F#团队发现了它 没有得到性能提升 当一些元组是值类型时 而不是引用类型。这个使 我们对自己的决定感觉更好 使用元组的引用类型。
答案 2 :(得分:7)
如果将.NET System.Tuple<...>类型定义为结构,则它们不具有可伸缩性。例如,一个长整数的三元组目前按如下方式扩展:
type Tuple3 = System.Tuple<int64, int64, int64>
type Tuple33 = System.Tuple<Tuple3, Tuple3, Tuple3>
sizeof<Tuple3> // Gets 4
sizeof<Tuple33> // Gets 4
如果将三元组定义为结构,则结果如下(基于我实现的测试示例):
sizeof<Tuple3> // Would get 32
sizeof<Tuple33> // Would get 104
由于元组在F#中具有内置语法支持,并且它们在这种语言中经常使用,“struct”元组会使F#程序员面临编写低效程序的风险,甚至不知道它。这很容易发生:
let t3 = 1L, 2L, 3L
let t33 = t3, t3, t3
在我看来,“struct”元组会导致在日常编程中产生显着低效的高概率。另一方面,正如@Jon所提到的,当前存在的“类”元组也会导致某些低效率。但是,我认为结构的“发生概率”乘以“潜在损害”的乘积会比目前的类别高得多。因此,目前的实施是较小的邪恶。
理想情况下,会有“class”元组和“struct”元组,两者都在F#中具有语法支持!
编辑(2017-10-07)
现在完全支持struct元组,如下所示:
答案 3 :(得分:4)
对于2元组,您仍然可以始终使用KeyValuePair&lt; TKey,TValue&gt;从早期版本的Common Type System。这是一种价值类型。
对Matt Ellis文章的一个小小的澄清是,当不可变性生效时,引用和值类型之间的使用语义差异只是“轻微的”(当然,这将是这种情况)。尽管如此,我认为在BCL设计中最好不要引入将元组交叉到某个阈值的引用类型的混淆。
答案 4 :(得分:0)
我不知道,但如果你曾经使用过F#元组是该语言的一部分。如果我创建了一个.dll并返回了一种类型的元组,那么有一个类型可以放入它。我现在怀疑F#是该语言的一部分(.Net 4)对CLR进行了一些修改以适应一些常见的结构在F#
来自http://en.wikibooks.org/wiki/F_Sharp_Programming/Tuples_and_Records
let scalarMultiply (s : float) (a, b, c) = (a * s, b * s, c * s);;
val scalarMultiply : float -> float * float * float -> float * float * float
scalarMultiply 5.0 (6.0, 10.0, 20.0);;
val it : float * float * float = (30.0, 50.0, 100.0)