Scala 和 JMM:数字类型性能

Scala and JMM: number types performance

我是 Scala 新手,不了解一些基础知识。

Scala 不包含原语。因此,int、short 和其他“简单”数字类型都是对象。因此,根据 JMM 的说法,它们不在堆栈中并且不受 GB 的清洁。在某些情况下,按 GB 清理可能过于昂贵。

所以我不太明白,为什么 Scala 被认为比 Java(其中基元位于堆栈中)更快。

它们的处理方式与 Java 处理这些类型的方式相同,它们仅在绝对必要时才装箱。关于如何以及何时装箱的细节可能不同,但编译器会尽可能使用原始表示。这是文档所说的(这仅适用于 Int,但它也适用于其他“原始”类型):

Int, a 32-bit signed integer (equivalent to Java's int primitive type) is a subtype of scala.AnyVal. Instances of Int are not represented by an object in the underlying runtime system.

There is an implicit conversion from scala.Int => scala.runtime.RichInt which provides useful non-primitive operations.

https://www.scala-lang.org/api/2.13.6/scala/Int.html

真正的主要区别在于,没有 两种不同的类型,如 Java 中那样,用于表示装箱和未装箱的表示 — 两者都得到相同的 Int 类型,而 Java 有 intInteger.

Scala does not contains primitives. Hence int, short and other "simple" number types are objects.

没错。

So, according to JMM,

Java 内存模型适用于 Java。它与 Scala 完全无关。

they are not located at stack and subject to cleaning by GB. Cleaning by GB may be too expensive for some cases.

Scala 中没有“堆栈”这样的东西。 Scala Language Specification 只在极少数地方提到了术语“堆栈”,其中 none 与 Int 有任何关系:

  • section 1 Lexical Syntax, subsection 1.6 XML mode中,据说因为XML字面量和Scala代码可以任意嵌套,解析器不得不使用栈数据结构来跟踪上下文。

  • section 7 Implicits, subsection 7.2 Implicit parameters中,据说为了防止在搜索隐式时无限递归,编译器保留了一堆“开放类型”,这是它当前正在搜索的类型隐式 for.

  • section 6 Expressions, subsection 6.6 Function Applications中,有如下语句,指定了Proper Direct Tail Recursion:

    A function application usually allocates a new frame on the program's run-time stack. However, if a local method or a final method calls itself as its last action, the call is executed using the stack-frame of the caller.

  • section 6 Expressions, subsection 6.20 Return Expressions中,关于嵌套函数的非本地returns的一种可能的实现策略有如下陈述:

    Returning from the method from within a nested function may be implemented by throwing and catching a scala.runtime.NonLocalReturnControl. Any exception catches between the point of return and the enclosing methods might see and catch that exception. A key comparison makes sure that this exception is only caught by the method instance which is terminated by the return.

    If the return expression is itself part of an anonymous function, it is possible that the enclosing method m has already returned before the return expression is executed. In that case, the thrown scala.runtime.NonLocalReturnControl will not be caught, and will propagate up the call stack.

在这 4 个实例中,前 2 个显然 指调用堆栈的概念,而是指通用计算机科学数据结构。第 4 个只是一个可能的实现策略的示例(“从嵌套函数 中的方法返回 可能 由 […] 实现”)。只有第三个实际上是相关的,因为它确实在谈论调用堆栈。但是,它没有说明分配 Ints 的任何内容,并且通过声明“通常”函数应用程序导致堆栈帧的分配,但它没有明确地为替代实现敞开大门,但没有到.

So I don't clearly understand, why Scala is considered faster than Java (in which primitives located in stack).

实际上,Java 语言规范中也没有任何内容表明原语位于堆栈上。事实上,Java 语言规范根本不要求堆栈的存在。在没有堆栈的情况下实现 Java 是完全合法的。

the JLS 中,术语“堆栈”恰好 出现 次。 多次提到“堆”一词,但 在复合词“堆污染”中,它只是描述一个词类型系统中的某个缺陷,但不一定需要堆,也不强制堆。

并且 none 这些提到的“堆污染”与原语有关。

请注意,当我说 Scala 语言规范未提及堆栈或堆或 Int 的分配方式时,这实际上非常重要。 因为 SLS 什么也没说,允许实现者做任何他们想做的事,包括 制作Ints 原始数据并分配它们在堆栈上。

这正是大多数 Scala 实现所做的。 (现已解散)Scala.NET 作为 .NET System.Int32 实施 scala.IntScala-native implements scala.Int as a C int32_t. Scala.js implements scala.Int as an ECMAScript number. And Scala-JVMscala.Int 实现为 JVM int

如果您在 Scala-JVM 存储库中查看 scala.Int 的源代码(src/library/scala/Int.scala), you will find that it is actually empty! More precisely, it only contains documentation 和声明,但没有定义或实现。另外,class 被标记为 final(意思是它不能被继承)和abstract(意思是它必须被继承,以便为缺少的实现提供覆盖),这是矛盾的。

这是如何工作的?好吧,编译器知道 Int 是什么以及它是如何工作的,它只是生成正确的代码来处理 JVM int。因此,当它看到对 scala.Int.+ 的调用时,它知道它必须生成一个 iadd bytecode instruction。同样,Scala-native 只会生成本机整数加法指令,等等。

换句话说,Int在语义上定义为对象,但实际上实用地实现为原语。

这是语言规范如何工作的一般规则:通常,它们只描述程序员看到的结果是什么,但它们留给实现者或如何实际实现该结果。因此,SLS 指定 Int 必须 看起来 就好像它实际上 一个对象,但实际上没有任何说明必须成为一个。