我什么时候应该在 Chapel 中使用 Record 与 Class
When should I use a Record vs a Class in Chapel
什么时候在 Chapel 中使用 Record
类型与 Class 比较有利?在 IRC 上有人提到 Records 在 locale 上分布得更好什么的。
Records 和 classes 在 Chapel 中的相似之处在于它们都支持创建具有字段和方法的对象。也就是说,也存在一些主要差异。这是对其中一些差异的快速回顾(tl;dr: 记录大致类似于 C 结构,而 classes 支持 class 层次结构、动态调度等。 ),然后是一个摘要,其中给出了一些指示,说明您何时可以选择使用其中之一:
变量的性质
- 记录变量直接表示一个记录对象
- class 变量表示对 class 对象的引用(指针)
换句话说,记录变量就是记录对象,所以两者没有区别。相反,class 变量指向一个 class 对象,该对象可能存储在与变量本身不同的区域(即,class 变量可以引用存储在分布式内存中的对象)。
这意味着当一个记录分配给另一个记录时,RHS 记录的值被复制到 LHS 记录的值(例如,通常 RHS 记录的字段被复制到 LHS 的字段记录)。另一个含义是记录必须定义(或使用编译器提供的)复制初始化器,并且通常提供 0 参数初始化器来建立新的记录对象。
相反,当一个 class 被分配给另一个时,它使 LHS class 变量引用(指向)与 RHS class 变量相同的对象。此外,因为 class 变量是指针,它们可以选择将 nil
值存储为标记(尽管 Chapel 还支持非 nilable class 变量,以避免运行时检查的需要或零指针取消引用的风险)。
Allocation/Deallocation
- class 对象分配在堆上,可以通过各种内存管理策略进行管理
- 记录对象“就地”分配(例如,在当前堆栈帧上)并自动取消分配(例如,当它们超出范围时)
这意味着 class 对象的生命周期可以独立于程序的结构,它依赖于几种策略之一来管理其内存(owned
、shared
, borrowed
, unmanaged
).相比之下,可以将记录对象视为始终自动进行内存管理,但受词法范围限制。
作为一个具体的例子,如果你有一个 classes 的数组,你通常会创建一个连续的指针序列,这些指针可以引用系统上任何地方的对象(本地堆或其他一些语言环境的堆)。而如果您有一个记录数组,它的存储通常是连续的记录对象序列。
这意味着如果你想在 Chapel 中创建一个“基于指针”的数据结构——比如链表、树或图——你通常会使用 class 对象来存储该数据结构的节点具有 class 类型的字段以引用它们的邻居(而使用记录来表示这些节点将具有挑战性,因为 Chapel 没有指针并且它们是就地分配的;换句话说,一个Chapel 记录不能包含其自身类型的字段,因为它会有效地导致无限递归数据结构)。
继承/方法分派
- 一个class可以声明为另一个class的子class,支持虚方法/动态调度
- 记录不支持对象层次结构,也不支持 virtual/dynamic 分派
这意味着如果你想像在 Java 或 C++ 中那样创建一个对象层次结构,你将需要使用 classes,以及这种 class 层次结构的初始值设定项本身是分层的。
总结
鉴于这些区别,您通常希望使用 class 如果:
- 您想创建一个“基于指针”的数据结构(例如,链表或二叉树),因为 class 类型可以使用 class 变量字段来引用自身/彼此
- 您想创建支持动态方法分派的对象层次结构
- 您想使用不受词法范围限制的对象
- 您想使用身份重要的对象
如果出现以下情况,您通常会希望使用记录:
- 价值比身份更重要,可以复制该价值
- 您希望对象内存由变量的范围管理(虽然
owned
classes 也可以有这种效果)
- 您希望更好地控制对象在内存中的布局方式
在实践中,将两者结合起来会非常强大。例如,具有 class 字段的记录可用于通过让 class 实现对象的身份来创建引用计数对象,并且该记录通过赋值重载和复制初始值设定项来实现引用计数语义处理记录的副本进入和离开作用域、被分配等情况(但是,请注意 Chapel 中的 shared
classes 直接提供了此功能,因此这只是一个示例,而不是常见做法)。
什么时候在 Chapel 中使用 Record
类型与 Class 比较有利?在 IRC 上有人提到 Records 在 locale 上分布得更好什么的。
Records 和 classes 在 Chapel 中的相似之处在于它们都支持创建具有字段和方法的对象。也就是说,也存在一些主要差异。这是对其中一些差异的快速回顾(tl;dr: 记录大致类似于 C 结构,而 classes 支持 class 层次结构、动态调度等。 ),然后是一个摘要,其中给出了一些指示,说明您何时可以选择使用其中之一:
变量的性质
- 记录变量直接表示一个记录对象
- class 变量表示对 class 对象的引用(指针)
换句话说,记录变量就是记录对象,所以两者没有区别。相反,class 变量指向一个 class 对象,该对象可能存储在与变量本身不同的区域(即,class 变量可以引用存储在分布式内存中的对象)。
这意味着当一个记录分配给另一个记录时,RHS 记录的值被复制到 LHS 记录的值(例如,通常 RHS 记录的字段被复制到 LHS 的字段记录)。另一个含义是记录必须定义(或使用编译器提供的)复制初始化器,并且通常提供 0 参数初始化器来建立新的记录对象。
相反,当一个 class 被分配给另一个时,它使 LHS class 变量引用(指向)与 RHS class 变量相同的对象。此外,因为 class 变量是指针,它们可以选择将 nil
值存储为标记(尽管 Chapel 还支持非 nilable class 变量,以避免运行时检查的需要或零指针取消引用的风险)。
Allocation/Deallocation
- class 对象分配在堆上,可以通过各种内存管理策略进行管理
- 记录对象“就地”分配(例如,在当前堆栈帧上)并自动取消分配(例如,当它们超出范围时)
这意味着 class 对象的生命周期可以独立于程序的结构,它依赖于几种策略之一来管理其内存(owned
、shared
, borrowed
, unmanaged
).相比之下,可以将记录对象视为始终自动进行内存管理,但受词法范围限制。
作为一个具体的例子,如果你有一个 classes 的数组,你通常会创建一个连续的指针序列,这些指针可以引用系统上任何地方的对象(本地堆或其他一些语言环境的堆)。而如果您有一个记录数组,它的存储通常是连续的记录对象序列。
这意味着如果你想在 Chapel 中创建一个“基于指针”的数据结构——比如链表、树或图——你通常会使用 class 对象来存储该数据结构的节点具有 class 类型的字段以引用它们的邻居(而使用记录来表示这些节点将具有挑战性,因为 Chapel 没有指针并且它们是就地分配的;换句话说,一个Chapel 记录不能包含其自身类型的字段,因为它会有效地导致无限递归数据结构)。
继承/方法分派
- 一个class可以声明为另一个class的子class,支持虚方法/动态调度
- 记录不支持对象层次结构,也不支持 virtual/dynamic 分派
这意味着如果你想像在 Java 或 C++ 中那样创建一个对象层次结构,你将需要使用 classes,以及这种 class 层次结构的初始值设定项本身是分层的。
总结
鉴于这些区别,您通常希望使用 class 如果:
- 您想创建一个“基于指针”的数据结构(例如,链表或二叉树),因为 class 类型可以使用 class 变量字段来引用自身/彼此
- 您想创建支持动态方法分派的对象层次结构
- 您想使用不受词法范围限制的对象
- 您想使用身份重要的对象
如果出现以下情况,您通常会希望使用记录:
- 价值比身份更重要,可以复制该价值
- 您希望对象内存由变量的范围管理(虽然
owned
classes 也可以有这种效果) - 您希望更好地控制对象在内存中的布局方式
在实践中,将两者结合起来会非常强大。例如,具有 class 字段的记录可用于通过让 class 实现对象的身份来创建引用计数对象,并且该记录通过赋值重载和复制初始值设定项来实现引用计数语义处理记录的副本进入和离开作用域、被分配等情况(但是,请注意 Chapel 中的 shared
classes 直接提供了此功能,因此这只是一个示例,而不是常见做法)。