scala vs java,性能和内存?

时间:2011-05-05 16:57:06

标签: java performance scala memory

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

8 个答案:

答案 0 :(得分:250)

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);
}

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

val bigEnough = array.filter(_.length > 2).flatMap(mapping.get)

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

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

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

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

答案 1 :(得分: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));
  }
}

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

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

现在,我不仅节省了输入完整变量名称和大括号的时间(让我花费5秒钟来思考深度算法思想),但我也可以在混淆竞赛中输入我的代码并可能获得假期的额外现金。

答案 2 :(得分:63)

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

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

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

所以,如果

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

您是否会说Scala代码现在相对慢了25%,或者快3倍?

正确答案取决于您如何定义“表现”:)

答案 3 :(得分:31)

计算机语言基准游戏:

Speed test java / scala 1.71 / 2.25

Memory test java / scala 66.55 / 80.81

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

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

底线:如果Scala让您和您的团队(以及当您离开时让项目结束的人员)提高效率,那么您应该去实现它。

答案 4 :(得分:20)

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

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

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

延期设计功能:

延期实施功能:

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

  • 线程安全的惰性值
  • 传递按姓名
  • Monadic stuff

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


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

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

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

没有中间集合,没有Option个实例等。 这也保留了集合类型,因此bigEnough的类型为Array[File] - Array的{​​{1}}实现可能会像Kerr先生的Java代码那样做。

上面列出的延迟设计功能还允许Scala的集合API开发人员在未来的版本中实现快速的特定于阵列的集合实现,而不会破坏API。这就是我所说的走向速度的道路。

此外:

collect

我在这里使用的val bigEnough = array.withFilter(_.length > 2).flatMap(mapping.get) 方法而不是withFilter修复了中间收集问题,但仍然存在Option实例问题。


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

在Java中,我们可能会写一些类似的东西:

filter

在Scala中,这只是:

if (logger.isDebugEnabled())
    logger.debug("trace");

因为在Scala中调试的message参数的类型为“logger.debug("trace") ”,我认为它是一个无参数函数,在评估它时会执行,但文档会调用pass-by-name。 / p>

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

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

对我来说,这是Scala中的一致主题。


硬代码无法捕捉到为什么Scala速度更快,尽管它有点暗示。

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

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

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

答案 5 :(得分:11)

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

工具:

很棒的博文:

答案 6 :(得分:10)

Java和Scala都编译成JVM字节码,所以区别不大。您可以获得的最佳比较可能是computer language benchmarks game,这实际上表明Java和Scala都具有相同的内存使用量。在列出的一些基准测试中,Scala仅比略慢慢,但这可能只是因为程序的实现不同。

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

答案 7 :(得分:4)

Java示例实际上不是典型应用程序的习惯用语。 这种优化的代码可以在系统库方法中找到。但是它会使用正确类型的数组,即File [],并且不会抛出IndexOutOfBoundsException。 (用于计数和添加的不同过滤条件)。 我的版本将是(总是(!)用花括号,因为我不想花一个小时来搜索一个错误,这个错误是通过在Eclipse中保存2秒来点击一个密钥而引入的):

List<File> bigEnough = new ArrayList<File>();
for(String s : array) {
  if(s.length() > 2) {
    File file = mapping.get(s);
    if (file != null) {
      bigEnough.add(file);
    }
  }
}

但我可以从我当前的项目中为您带来许多其他丑陋的Java代码示例。我试图通过分解常见的结构和行为来避免常见的复制和修改编码风格。

在我的抽象DAO基类中,我有一个用于公共缓存机制的抽象内部类。对于每个具体的模型对象类型,都有一个抽象DAO基类的子类,其中内部类是子类,以便为从数据库加载时创建业务对象的方法提供实现。 (我们不能使用ORM工具,因为我们通过专有API访问另一个系统。)

这个子类化和实例化代码在Java中根本不清楚,在Scala中非常易读。