为什么OCaml中的int只有31位?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (14)

还没有在其他地方看过这个“功能”。我知道第32位用于垃圾收集。但是为什么只有整数而不是其他基本类型才这样呢?

提问于
用户回答回答于

这被称为带标签的指针表示,并且是几十年来在许多不同的解释器,虚拟机和运行时系统中使用的相当常见的优化技巧。几乎每个Lisp实现都使用它们,许多Smalltalk VM,许多Ruby解释器等等。

通常,在这些语言中,你总是传递指向对象的指针。一个对象本身由一个对象头部组成,该对象头部包含对象元数据(如对象的类型,类,可能访问控制限制或安全注释等),然后是实际的对象数据本身。所以,一个简单的整数将被表示为一个指针加上一个由元数据和实际整数组成的对象。即使使用非常紧凑的表示形式,对于一个简单的整数而言,这也是6字节。

另外,你不能将这样的整数对象传递给CPU来执行快速整数运算。如果你想添加两个整数,你实际上只有两个指针,它们指向你想要添加的两个整数对象的对象头的开头。因此,首先需要对第一个指针执行整数运算,以将偏移量添加到存储整数数据的对象中。然后你必须取消这个地址的引用。用第二个整数再次做同样的事情。现在你有两个整数,你可以要求CPU添加。当然,您现在需要构造一个新的整数对象来保存结果。

所以,为了执行一个整数加法,你实际上需要执行三个整数加法加上两个指针dererefences加上一个对象构造。而你占用了近20个字节。

但是,诀窍是,所谓的不可变的值类型喜欢整数,你通常不会需要的所有元数据对象中的标头:你可以把所有的东西出来,简单地合成它(这是VM-nerd-代表“假”),当有人关心看。一个整数将始终有类Integer,不需要单独存储该信息。如果有人使用反射来找出类的整数,你只需回复Integer,没有人会知道,你实际上并没有存储在对象头标信息,并在事实上,不是连对象头(或目的)。

因此,关键是要存储值指针内的对象对象,有效地坍塌两成一个。

有一些CPU实际上在一个指针内有额外的空间(所谓的标签位),允许你在指针本身中存储有关指针的额外信息。额外的信息,如“这实际上不是一个指针,这是一个整数”。例子包括Burroughs B5000,各种Lisp机器或AS / 400。不幸的是,目前大多数主流CPU都没有这个功能。

然而,有一条出路:当地址不与字边界对齐时,大多数当前主流CPU的工作速度明显较慢。有些甚至根本不支持未对齐的访问。

这意味着在实践中,所有指针都可以被4整除,这意味着它们总是以两位结束0。这使我们能够区分实际指针(即结束于00)和实际上是伪装整数(以此结尾)的指针1。它仍然留给我们所有可以10免费做其他事情的指针。另外,大多数现代操作系统为自己保留了非常低的地址,这给我们另一个混乱的区域(以24 0秒开始并以此结束的指针00)。

因此,您可以将一个31位整数编码为一个指针,只需将其向左移1位并添加1即可。而且你可以用它们执行非常快的整数运算,只需简单地将它们移位(有时甚至不需要)。

我们如何处理这些其他地址空间?那么,典型的例子包括编码float在其他大的地址空间S和数量之类的特殊对象truefalsenil,127个ASCII字符,一些常用的短字符串,空列表,空的对象,空数组等附近的0地址。

例如,在MRI,YARV和Rubinius Ruby解释器中,整数以我上面描述的方式false被编码,被编码为地址0(它恰好也是false C中的表示),true作为地址2(恰好如此true移位一位的C表示)和nilas 4

用户回答回答于

请参阅https://ocaml.org/learn/tutorials/performance_and_profiling.html中的“整数,标记位,堆分配值的表示”一节,以获得很好的说明。

简单的答案是它是为了表现。将参数传递给函数时,它将作为整数或指针传递。在机器级语言级别,无法确定寄存器是否包含整数或指针,它只是一个32或64位值。所以OCaml运行时检查标记位以确定它收到的是一个整数还是一个指针。如果标记位已设置,则该值为整数,并传递给正确的过载。否则它是一个指针,查找类型。

为什么只有整数有这个标签?因为一切都是作为指针传递的。传递的内容是整数或指向某种其他数据类型的指针。只有一个标签位,只能有两种情况。

扫码关注云+社区