scala vs java,性能和内存?

Joh*_*ith 158 java memory performance scala

我很想研究Scala,并且有一个基本问题我似乎无法找到答案:一般来说,Scala和Java之间的内存性能和使用是否存在差异?

Rex*_*err 254

Scala使得很容易在没有意识到的情况下使用大量内存.这通常非常强大,但偶尔会很烦人.例如,假设您有一个字符串数组(称为array),以及从这些字符串到文件(称为mapping)的映射.假设您想要获取地图中的所有文件,并且来自长度大于2的字符串.在Java中,你可能会

int n = 0;
for (String s: array) {
  if (s.length > 2 && mapping.containsKey(s)) n++;
}
String[] bigEnough = new String[n];
n = 0;
for (String s: array) {
  if (s.length <= 2) continue;
  bigEnough[n++] = map.get(s);
}
Run Code Online (Sandbox Code Playgroud)

呼!努力工作.在Scala中,执行相同操作的最紧凑方式是:

val bigEnough = array.filter(_.length > 2).flatMap(mapping.get)
Run Code Online (Sandbox Code Playgroud)

简单!但是,除非你非常熟悉集合的工作方式,否则你可能没有意识到这样做是为了创建一个额外的中间数组(带filter),并为数组的每个元素创建一个额外的对象(带有mapping.get,返回)一个选项).它还创建了两个函数对象(一个用于过滤器,一个用于flatMap),尽管由于函数对象很小,这很少是一个主要问题.

所以基本上,内存使用在原始级别是相同的.但是Scala的库有许多强大的方法,可以让你很容易地创建大量(通常是短命的)对象.垃圾收集器通常对这种垃圾非常好,但是如果你完全忘记了正在使用的内存,你可能会在Scala中遇到麻烦而不是Java.

请注意,计算机语言基准测试游戏Scala代码是以类似Java的方式编写的,以便获得类似Java的性能,因此具有类似Java的内存使用.您可以在Scala中执行此操作:如果您编写的代码看起来像高性能Java代码,那么它将是高性能的Scala代码.(您可以用更惯用的Scala风格编写它,但仍然可以获得良好的性能,但这取决于具体细节.)

我应该补充说,每花费一定的编程时间,我的Scala代码通常比我的Java代码更快,因为在Scala中我可以用更少的工作量完成繁琐的非性能关键部分,并且更多的注意力来优化算法和性能关键部分的代码.

  • 最后一段+1.这是一个至关重要的问题,因为经常不考虑*. (168认同)
  • 我认为[views](http://www.scala-lang.org/docu/files/collections-api/collections_42.html)对你提到的问题有很大的帮助.或者对阵列不是这样,特别是? (2认同)
  • @Kevin Wright - “这是一个经常被忽视的关键点” - 这句话说起来容易,但很难证明,告诉我们一些关于 Rex Kerr 的技能,而不是其他不那么熟练的人所取得的成就。 (2认同)
  • @RexKerr - 你的Java示例不会为每个可能的String查找映射键两次,你的Scala示例只在选择了字符串后才执行一次吗?即它们是否针对不同的数据集以不同的方式进行了优化? (2认同)
  • 使用 java 8 流,差异很小,我认为 (2认同)
  • 这里的每个人都将 Scala 与 Java7 或更早版本进行比较。与java8比较,你会发现Java8比Scala快得多 (2认同)

Not*_*ing 100

我是新用户,所以我无法在上面给Rex Kerr的答案添加评论(允许新用户"回答"而不是"评论"是一个非常奇怪的规则btw).

我注册时只是为了回应"phew,Java是如此冗长和如此艰苦的工作"暗示Rex上面流行的答案.虽然您当然可以编写更简洁的Scala代码,但给出的Java示例显然很臃肿.大多数Java开发人员会编写如下代码:

List<String> bigEnough = new ArrayList<String>();
for(String s : array) {
  if(s.length() > 2 && mapping.get(s) != null) {
    bigEnough.add(mapping.get(s));
  }
}
Run Code Online (Sandbox Code Playgroud)

当然,如果我们假装Eclipse没有为你做大部分的实际打字,并且每个保存的字符真的让你成为一个更好的程序员,那么你可以编写代码:

List b=new ArrayList();
for(String s:array)
  if(s.length()>2 && mapping.get(s) != null) b.add(mapping.get(s));
Run Code Online (Sandbox Code Playgroud)

现在,我不仅救我花了输入完整的变量名和大括号(释放我花5秒钟思考深算法的思想)的时间,但我还可以在混淆比赛进入我的代码,并可能赚取外快假期.

  • 很好!我厌倦了人为的例子,其中膨胀的Java代码后面是Scala(或其他一些FP语言)的一些精心构造的简洁示例,然后匆匆得出结论,因为Scala必须比Java好.无论如何谁曾在Scala写过任何重要内容!;-)并且不要说Twitter ... (20认同)
  • 你怎么不是"月度嘻哈语"俱乐部的成员?好评.我特别喜欢阅读最后一段. (7认同)
  • 我们在它,在java8中它将是:`Arrays.stream(array).map(mapping :: get).filter(x-> x!= null).toArray(File [] :: new);` (5认同)
  • 好吧,Rex的解决方案为数组预分配内存,这将使_compiled code_运行得更快(因为使用您的方法,您可以让JVM在数组增长时定期重新分配您的数组).即使涉及更多的打字,性能方面也可能是赢家. (2认同)
  • 是什么让Scala在某些方面比Java"更好"的是扩展的类型系统功能,这使得更容易将更多通用模式表达为类型(如Monads,Functors等).这允许您创建由于过于严格的合同而不会妨碍您的类型,这在Java中经常发生.严格的合同不是基于代码中的实际模式,因此只需要对代码进行适当的单元测试就需要Inversion of Responsibility模式(首先要考虑Dependence Injection和它带来的XML Hell).addl.灵活带来的简洁只是一个奖励. (2认同)

Kev*_*ght 65

像Java一样编写Scala,你可以期望发出几乎相同的字节码 - 几乎相同的指标.

使用不可变对象和更高阶函数更"惯用"地写它,它会稍微慢一些.这个经验法则的一个例外是当使用类型params使用@specialised注释的通用对象时,这将创建甚至更大的字节码,通过避免装箱/取消装箱可以超过Java的性能.

另外值得一提的是,在编写可以并行运行的代码时,更多内存/更低速度是不可避免的权衡.惯用Scala代码在本质上远比典型的Java代码更具说明性,并且通常只有4个字符(.par)远离完全并行.

因此,如果

  • 在单个线程中,Scala代码比Java代码长1.25倍
  • 它可以很容易地分成4个核心(现在甚至在笔记本电脑中也常见)
  • 对于原始Java的并行运行时间(1.24/4 =)0.3125x

那么你会说Scala代码现在相对慢了25%,还是快了3倍?

正确的答案取决于你如何定义"性能":)

  • >>那么你会说Scala代码现在相对慢了25%,还是快了3倍?<<我会说为什么不是你对多线程Java代码的假设比较? (26认同)
  • @igouy - 重点是所说的假设代码不存在,"更快"的Java代码的命令本质使得并行化更加困难,因此成本/收益比意味着它根本不可能发生.另一方面,习惯性Scala在本质上更具说明性,通常可以同时进行,而不仅仅是一个微不足道的变化. (17认同)
  • 并发Java程序的存在并不意味着*典型的*Java程序可以很容易地适应并发.如果有的话,我会说特定的fork-join风格在Java中特别少见,并且必须进行显式编码,而简单的操作,例如查找最小包含值,或者集合中值的总和,可以平行地完成在Scala中只需使用`.par`. (7认同)
  • 不,我可能不会.这种事情是许多算法的基本构建块,并且看到它在语言和标准库中存在如此低的水平(所有程序将使用的标准库,而不仅仅是典型的),证明你是'通过简单地选择语言就已经接近并发了.例如,映射集合本质上适合于并行化,并且不使用`map`方法的Scala程序的数量将非常小. (5认同)
  • 顺便提一下,你可能想提一下`.par`是2.9. (4认同)

Pet*_*ego 31

计算机语言基准游戏:

速度测试 java/scala 1.71/2.25

内存测试 java/scala 66.55/80.81

因此,这个基准测试表明java的速度提高了24%,scala的内存增加了21%.

总而言之,这并不重要,在真实世界的应用程序中无关紧要,因为大部分时间都是由数据库和网络消耗的.

一句话:如果Scala让你和你的团队(以及你离开时让项目结束的人)更有成效,那么你应该去做.

  • [代码大小](http://shootout.alioth.debian.org/u64q/which-language-is-best.php?calc=chart&gpp=on&ghc=on&csharp=on&sbcl=on&yarv=on&python3=on&java=on&hipe=on&xfullcpu=0&xmem = 0&xloc = 1&nbody = 1&fannkuchredux = 1&meteor = 0&fasta = 1&spectralnorm = 1&revcomp = 1&mandelbrot = 1&knucleotide = 1&regexdna = 1&pidigits = 1&chameneosredux = 0&threadring = 0&binarytrees = 1)java/scala 3.39/2.21 (33认同)
  • 小心这些数字,它们听起来非常精确,而实际上它们几乎没有任何意义.并不是说Scala平均比Java平均快24%等等. (22认同)
  • "如果Scala让你和你的团队......"底线:你会知道之前没有:-) (9认同)
  • Afaik引用的数字表明相反:Java比scala快24%.但正如你所说 - 它们是微基准测试,不需要与真实应用程序中发生的情况相匹配.而不同语言的不同方式或问题解决方案最终可能导致可比性较低的程序. (3认同)

Set*_*eth 20

其他人已经就紧密循环回答了这个问题,尽管Rex Kerr的例子之间似乎有明显的性能差异,我已经评论过了.

这个答案的确针对那些可能会将紧密循环优化作为设计缺陷进行调查的人.

我对Scala相对较新(大约一年左右),但到目前为止,它的感觉是它允许你相对容易地推迟设计,实现和执行的许多方面(有足够的背景阅读和实验:)

延期设计特点:

延期实施功能:

延期执行功能:(抱歉,没有链接)

  • 线程安全的惰性值
  • 传址名称
  • Monadic的东西

对我来说,这些功能可以帮助我们走上快速,紧凑的应用之路.


Rex Kerr的例子在延迟执行的哪些方面有所不同.在Java示例中,延迟分配内存,直到计算出Scala示例延迟映射查找的大小.对我来说,它们似乎是完全不同的算法.

以下是我认为更像是苹果与苹果等效的Java示例:

val bigEnough = array.collect({
    case k: String if k.length > 2 && mapping.contains(k) => mapping(k)
})
Run Code Online (Sandbox Code Playgroud)

没有中间集合,没有Option实例等.这也保留了集合类型,所以bigEnough类型是Array[File]- Arraycollect实现可能会像Kerr先生的Java代码那样做.

上面列出的延迟设计功能还允许Scala的集合API开发人员在将来的版本中实现快速的特定于Array的集合实现,而不会破坏API.这就是我所指的踩着速度之路.

也:

val bigEnough = array.withFilter(_.length > 2).flatMap(mapping.get)
Run Code Online (Sandbox Code Playgroud)

withFilter我在这里使用的方法不是filter修复中间收集问题,但仍然存在Option实例问题.


Scala中简单执行速度的一个例子是日志记录.

在Java中,我们可能会写:

if (logger.isDebugEnabled())
    logger.debug("trace");
Run Code Online (Sandbox Code Playgroud)

在Scala中,这只是:

logger.debug("trace")
Run Code Online (Sandbox Code Playgroud)

因为在Scala中调试的消息参数具有类型" => String",我认为它是一个无参数函数,在评估它时会执行,但文档会调用pass-by-name.

编辑{Scala中的函数是对象,因此这里有一个额外的对象.对于我的工作,一个简单对象的重量值得消除日志消息被不必要地评估的可能性.}

这不会使代码更快,但它确实使它更快更快,而且我们不太可能拥有经历和整体清理其他人的代码的经验.

对我而言,这是Scala中的一致主题.


硬代码无法捕获为什么Scala更快,尽管它有点暗示.

我觉得这是Scala中代码重用和代码质量上限的结合.

在Java中,令人敬畏的代码经常被迫成为一个难以理解的混乱,因此在生产质量API中并不可行,因为大多数程序员都无法使用它.

我非常希望Scala可以允许我们中的爱因斯坦实现更有能力的API,可能通过DSL表达.Scala中的核心API已经走在了这条道路上.

  • 可预测的if语句(在超标量处理器上基本上是免费的)vs对象分配+垃圾.Java代码显然更快(注意它只评估条件,执行不会到达日志语句.)响应"对于我的工作,一个普通对象的权重值得消除日志消息被不必要地评估的可能性". (3认同)

olu*_*ies 11

@higherkinded关于这个主题的演示文稿 - Scala Performance Considerations,它进行了一些Java/Scala比较.

工具:

伟大的博文:


rye*_*guy 10

Java和Scala都编译成JVM字节码,所以区别并不大.您可以获得的最佳比较可能是计算机语言基准测试游戏,它基本上表示Java和Scala都具有相同的内存使用率.在列出的一些基准测试中,Scala仅略微慢于Java,但这可能只是因为程序的实现不同.

实际上,它们都非常接近,不值得担心.使用像Scala这样更具表现力的语言所获得的工作效率提升远远超过最小化(如果有的话)性能.

  • 我在这里看到了一个逻辑谬误:两种语言都编译成字节码,但是一个有经验的程序员和一个新手 - 他们的代码也编译成字节码 - 但不是相同的字节码,所以得出结论,差异不能那么大,可能是错的.事实上,在以前的时代,一个while循环在scala中可能比语义上等效的for循环要快得多(如果我没记错的话,它今天要好得多).当然,两者都被编译为字节码. (7认同)
  • 令人惊讶的错误答案与惊人的高票. (3认同)