scala 范围与大型集合上的列表性能

2023-12-28

我对 10,000,000 个元素运行了一组性能基准测试,我发现每次实现的结果都有很大差异。

任何人都可以解释为什么创建 Range.ByOne 会产生比简单的基元数组更好的性能,但将相同的范围转换为列表会导致比最坏情况场景更差的性能?

创建 10,000,000 个元素,并打印出以 1,000,000 为模的元素。 JVM 大小始终设置为相同的最小值和最大值:-Xms?m -Xmx?m

import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit._

object LightAndFastRange extends App {

def chrono[A](f: => A, timeUnit: TimeUnit = MILLISECONDS): (A,Long) = {
  val start = System.nanoTime()
  val result: A = f
  val end = System.nanoTime()
  (result, timeUnit.convert(end-start, NANOSECONDS))
}

  def millions(): List[Int] =  (0 to 10000000).filter(_ % 1000000 == 0).toList

  val results = chrono(millions())
  results._1.foreach(x => println ("x: " + x))
  println("Time: " + results._2);
}

JVM大小为27m,耗时141毫秒

相比之下,转换为 List 会显着影响性能:

import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit._

object LargeLinkedList extends App {
  def chrono[A](f: => A, timeUnit: TimeUnit = MILLISECONDS): (A,Long) = {
  val start = System.nanoTime()
  val result: A = f
  val end = System.nanoTime()
  (result, timeUnit.convert(end-start, NANOSECONDS))
}

  val results = chrono((0 to 10000000).toList.filter(_ % 1000000 == 0))
  results._1.foreach(x => println ("x: " + x))
  println("Time: " + results._2)
}

460-455 m 需要 8514-10896 ms

相反,这个 Java 实现使用原语数组

import static java.util.concurrent.TimeUnit.*;

public class LargePrimitiveArray {

    public static void main(String[] args){
            long start = System.nanoTime();
            int[] elements = new int[10000000];
            for(int i = 0; i < 10000000; i++){
                    elements[i] = i;
            }
            for(int i = 0; i < 10000000; i++){
                    if(elements[i] % 1000000 == 0) {
                            System.out.println("x: " + elements[i]);
                    }
            }
            long end = System.nanoTime();
            System.out.println("Time: " + MILLISECONDS.convert(end-start, NANOSECONDS));
    }
}

JVM 大小为 59m,耗时 116ms

Java 整数列表

import java.util.List;
import java.util.ArrayList;
import static java.util.concurrent.TimeUnit.*;

public class LargeArrayList {

    public static void main(String[] args){
            long start = System.nanoTime();
            List<Integer> elements = new ArrayList<Integer>();
            for(int i = 0; i < 10000000; i++){
                    elements.add(i);
            }
            for(Integer x: elements){
                    if(x % 1000000 == 0) {
                            System.out.println("x: " + x);
                    }
            }
            long end = System.nanoTime();
            System.out.println("Time: " + MILLISECONDS.convert(end-start, NANOSECONDS));
    }

}

JVM 大小为 283m,耗时 3993 ms

我的问题是,为什么第一个示例如此高效,而第二个示例却受到如此严重的影响。我尝试创建视图,但未能成功重现该系列的性能优势。

所有测试均在 Mac OS X Snow Leopard 上运行, Java 6u26 64 位服务器 Scala 2.9.1.final

EDIT:

为了完成,这里是使用 LinkedList 的实际实现(就空间而言,这是比 ArrayList 更公平的比较,因为正如正确指出的那样,scala 的 List 是链接的)

import java.util.List;
import java.util.LinkedList;
import static java.util.concurrent.TimeUnit.*;

public class LargeLinkedList {

    public static void main(String[] args){
            LargeLinkedList test = new LargeLinkedList();
            long start = System.nanoTime();
            List<Integer> elements = test.createElements();
            test.findElementsToPrint(elements);
            long end = System.nanoTime();
            System.out.println("Time: " + MILLISECONDS.convert(end-start, NANOSECONDS));
    }

    private List<Integer> createElements(){
            List<Integer> elements = new LinkedList<Integer>();
            for(int i = 0; i < 10000000; i++){
                    elements.add(i);
            }
            return elements;
    }

    private void findElementsToPrint(List<Integer> elements){
            for(Integer x: elements){
                    if(x % 1000000 == 0) {
                            System.out.println("x: " + x);
                    }
            }
    }

}

需要 3621-6749 毫秒,480-460 mbs。这与第二个 scala 示例的性能更加一致。

最后,一个 LargeArrayBuffer

import collection.mutable.ArrayBuffer
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit._

object LargeArrayBuffer extends App {

 def chrono[A](f: => A, timeUnit: TimeUnit = MILLISECONDS): (A,Long) = {
  val start = System.nanoTime()
  val result: A = f
  val end = System.nanoTime()
  (result, timeUnit.convert(end-start, NANOSECONDS))
 }

 def millions(): List[Int] = {
    val size = 10000000
    var items = new ArrayBuffer[Int](size)
    (0 to size).foreach (items += _)
    items.filter(_ % 1000000 == 0).toList
 }

 val results = chrono(millions())
  results._1.foreach(x => println ("x: " + x))
  println("Time: " + results._2);
 }

大约需要 2145 毫秒和 375 MB

非常感谢您的回答。


哦,这里发生了很多事情!

让我们从Java开始int[]。 Java 中的数组是唯一不进行类型擦除的集合。的运行时表示int[]与运行时表示不同Object[],因为它实际上使用int直接地。因此,使用它时不涉及拳击。

在内存方面,内存中有 40.000.000 个连续字节,每当读取或写入一个元素时,每次都会读取和写入 4 个字节。

相比之下,一个ArrayList<Integer>-- 以及几乎任何其他通用集合 -- 由 40.000.000 或 80.000.00 个连续字节组成(分别在 32 位和 64 位 JVM 上),加上 80.000.000 个字节以 8 字节为一组分布在内存中。对元素的每次读取和写入都必须经过两个内存空间,当您正在执行的实际任务如此之快时,处理所有内存所花费的绝对时间非常重要。

那么,回到 Scala,对于第二个示例,您可以在其中操作List。现在,斯卡拉的List更像Java的LinkedList比名字严重错误的ArrayList。 a 的每个元素List由一个名为的对象组成Cons,它有 16 个字节,有一个指向该元素的指针和一个指向另一个列表的指针。所以,一个List10.000.000 个元素由 160.000.000 个以 16 字节为一组分布在内存中的元素组成,加上以 8 字节为一组分布在内存中的 80.000.000 个元素。那么什么才是正确的ArrayList更是如此List

最后,Range. A Range是具有下限和上限以及步长的整数序列。 ARange10.000.000 个元素的大小为 40 个字节:三个整数(非通用)用于表示下限、上限和步长,加上一些预先计算的值(last, numRangeElements) 和另外两个整数用于lazy val线程安全。只是为了明确一点,那就是NOT40 乘以 10.000.000:总共 40 个字节。范围的大小完全无关,因为它不存储单个元素。只是下限、上限和步长。

现在,因为一个Range is a Seq[Int],对于大多数用途来说,它仍然需要经过装箱:int将被转换成Integer然后回到int再次,可悲的是,这是浪费。

缺点尺寸计算

因此,这是对缺点的初步计算。首先,阅读本文 http://www.javamex.com/tutorials/memory/object_memory_usage.shtml关于对象占用多少内存的一些一般准则。重点是:

  1. Java 对普通对象使用 8 个字节,对对象数组使用 12 个字节,用于“内务管理”信息(该对象的类是什么,等等)。
  2. 对象以 8 字节块的形式分配。如果您的对象小于该值,则会对其进行填充以补充它。

我其实以为是16个字节,不是8个。反正Cons也比我想象的要小。其字段有:

public static final long serialVersionUID; // static, doesn't count
private java.lang.Object scala$collection$immutable$$colon$colon$$hd;
private scala.collection.immutable.List tl;

参考文献是at least4 个字节(在 64 位 JVM 上可能更多)。所以我们有:

8 bytes Java header
4 bytes hd
4 bytes tl

这使得它只有 16 个字节长。事实上,相当不错。在示例中,hd将指向一个Integer对象,我假设它有 8 个字节长。至于tl,它指出了另一个缺点,我们已经在计算了。

我将尽可能使用实际数据来修改估计值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

scala 范围与大型集合上的列表性能 的相关文章

  • Grails 2.3.0 自动重新加载不起作用

    我最近将我们的项目升级到 grails 2 3 0 一切工作正常 除了每当我更改代码时自动重新加载都无法工作的问题 这包括所有项目工件 控制器 域 服务 gsps css 和 javascript 文件 我的旧版本 grails 可以正常工
  • eclipse中导入项目文件夹图标

    我在 Eclipse 工作区中新导入的 Maven 项目有J and M项目文件夹顶部的图标 项目和包资源管理器 而其他导入的 Maven 项目只有一个J icon 有人可以解释其中的区别吗 该项目有J装饰器被称为 Java 项目和具有M装
  • Java套接字:在连接被拒绝异常时重试的最佳方法?

    现在我正在这样做 while true try SocketAddress sockaddr new InetSocketAddress ivDestIP ivDestPort downloadSock new Socket downloa
  • 记录骆驼路线

    我的项目中有几个 Camel 上下文 如果可能的话 我想以逆向工程方式记录路线 因为我们希望保持与上下文相关的文档最新 最好的方法是什么 我们倾向于预先实际设计路线 并使用来自EIP book http www eaipatterns co
  • 如何从 Retrofit2 获取字符串响应?

    我正在做 android 正在寻找一种方法来执行超级基本的 http GET POST 请求 我不断收到错误 java lang IllegalArgumentException Unable to create converter for
  • 在java中实现你自己的阻塞队列

    我知道这个问题之前已经被问过并回答过很多次了 但我只是无法根据互联网上找到的示例找出窍门 例如this http tutorials jenkov com java concurrency blocking queues html or t
  • 具有共享依赖项的多模块项目的 Gradle 配置

    使用 gradle 制作第一个项目 所以我研究了 spring gradle hibernate 项目如何组织 gradle 文件 并开始制作自己的项目 但是 找不到错误 为什么我的配置不起作用 子项目无法解决依赖关系 所以项目树 Root
  • 将表值参数与 SQL Server JDBC 结合使用

    任何人都可以提供一些有关如何将表值参数 TVP 与 SQL Server JDBC 一起使用的指导吗 我使用的是微软提供的6 0版本的SQL Server驱动程序 我已经查看了官方文档 https msdn microsoft com en
  • Git 无法识别重命名和修改的包文件

    我有一个名为的java文件package old myfile java 我已经通过 git 提交了这个文件 然后我将我的包重命名为new所以我的文件在package new myfile java 我现在想将此文件重命名 和内容更改 提交
  • Java 数组的最大维数

    出于好奇 在 Java 中数组可以有多少维 爪哇language不限制维数 但是JavaVM规范将维度数限制为 255 例如 以下代码将无法编译 class Main public static void main String args
  • Cloudfoundry:如何组合两个运行时

    cloundfoundry 有没有办法结合两个运行时环境 我正在将 NodeJS 应用程序部署到 IBM Bluemix 现在 我还希望能够执行独立的 jar 文件 但应用程序失败 APP 0 bin sh 1 java not found
  • Android Studio 将音乐文件读取为文本文件,如何恢复它?

    gameAlert mp3是我的声音文件 运行应用程序时 它询问我该文件不与任何文件类型关联 请定义关联 我选择TextFile错误地 现在我的音乐文件被读取为文本文件 我如何将其转换回music file protected void o
  • 如何配置 WebService 返回 ArrayList 而不是 Array?

    我有一个在 jax ws 上实现的 java Web 服务 此 Web 服务返回用户的通用列表 它运行得很好 Stateless name AdminToolSessionEJB RemoteBinding jndiBinding Admi
  • 如何在 Eclipse Java 动态 Web 项目中使用 .properties 文件?

    我正在 Eclipse 中开发动态 Web 项目 我创建了一个 properties 文件来存储数据库详细信息 用户名 密码等 我通过右键单击项目和 New gt File 添加它 我使用了Java util包Properties类 但它不
  • VBA Excel:将范围值分配给新范围

    我在将一个工作簿范围中的值分配给当前工作簿中的某个范围时遇到问题 当我使用 Range A1 C1 分配我的范围时 此代码工作正常 但是当我使用 Range Cells 1 1 Cells 1 3 定义我的范围时 该函数会失败 Sub Co
  • Linux 上有关 getBounds() 和 setBounds() 的 bug_id=4806603 的解决方法?

    在 Linux 平台上 Frame getBounds 和 Frame setBounds 的工作方式不一致 这在 2003 年就已经有报道了 请参见此处 http bugs java com bugdatabase view bug do
  • 对象锁定私有类成员 - 最佳实践? (爪哇)

    I asked 类似的问题 https stackoverflow com questions 10548066 multiple object locks in java前几天 但对回复不满意 主要是因为我提供的代码存在一些人们关注的问题
  • 如何在Java中正确删除数组[重复]

    这个问题在这里已经有答案了 我刚接触 Java 4 天 从我搜索过的教程来看 讲师们花费了大量精力来解释如何分配二维数组 例如 如下所示 Foo fooArray new Foo 2 3 但我还没有找到任何解释如何删除它们的信息 从内存的情
  • Java:拆箱整数时出现空指针异常?

    此代码导致空指针异常 我不知道为什么 private void setSiblings PhylogenyTree node Color color throws InvalidCellNumberException PhylogenyTr
  • Java的-XX:+UseMembar参数是什么

    我在各种地方 论坛等 看到这个参数 并且常见的答案是它有助于高并发服务器 尽管如此 我还是找不到 sun 的官方文档来解释它的作用 另外 它是Java 6中添加的还是Java 5中存在的 顺便说一句 许多热点虚拟机参数的好地方是这一页 ht

随机推荐

  • 将 mp4 视频保存到设备相机胶卷

    我从一个 NSString 开始 它是 mp4 文件的 url 从这里我希望将该视频保存到设备相机胶卷中 看来我只能保存 mov 文件 所以我必须首先转换 mp4 但我看到的关于此的几篇文章没有帮助 谁能帮助我完成这个任务 提前致谢 您可以
  • C# 使用 StreamReader 读取资源内的文本文件时出现 FileNotFound 异常

    using SteamReader sr new StreamReader text txt 我也尝试不将文本文件放在 resx file The StreamReader您正在使用的构造函数期望该文件存在于磁盘上 如果该文件嵌入到程序集中
  • 当我的应用程序关闭时,如何处理通知操作?

    问题概要 我正在编写一个 iOS 应用程序 它发送提醒通知 让用户通过 x callback url 运行其他应用程序 如果应用程序位于前台或后台 我的一切都可以完美运行 但当我的应用程序关闭时 它就无法运行 当我的应用程序关闭时 通知也会
  • 如何在 Xamarin 中重用相同的视图? XAML

    所以我得到了这段代码 我需要在或多或少的所有页面上重复使用 但是我有点厌倦了更改一个页面并且必须在 10 个或更多地方做同样的事情 有没有更好的方法做这个 使用 Xamarin Forms 也许可以使用自定义控制器或使用标记扩展在堆栈布局内
  • 使用 ISO V2 Coated 等颜色配置文件将 CMYK 颜色转换为 RGB?

    我知道这个问题之前已经以多种不同的方式提出过 但似乎没有一个与我的问题相关 我想转换一个CMYK准确地着色RGB使用颜色配置文件 例如ISO Coated V2 我想这样做 因为简单的数学转换会导致明亮的颜色无法在CMYK色彩空间 理想情况
  • 在 JSON 请求中发送图像

    我在用着JSON with REST用于使用 Web 服务的 api 现在我还需要根据请求发送图像 是否可以 如果是 我需要在客户端 服务器端进行哪些更改 在我的Java代码中 我应该如何发送图像内容 是否需要单独设置内容类型 执行此操作的
  • Python:无需 OpenCV 即可访问相机

    同志们 我想用 Python 从笔记本电脑摄像头捕获图像 目前所有迹象都指向 OpenCV 问题是 OpenCV 的安装是一场噩梦 而且每次您在新系统上重新安装代码时 这个噩梦都会再次发生 有没有更轻量级的方法在Python中捕获相机数据
  • Rails Paperclip S3 重命名数千个文件?

    我正在尝试重命名 s3 中的许多文件 更改当前的回形针has attached file path from stuff id updated at style extension to stuff id counter style ext
  • 为什么我的工具输出会覆盖自身以及如何修复它?

    这个问题的目的是要成为一个典范 https meta stackoverflow com questions 291992它涵盖了各种各样的问题 其答案归结为 你有 DOS 行结尾被输入到 Unix 工具中 任何有相关问题的人都应该清楚地解
  • 有没有办法将 gmpxx.h 与 c++98 一起使用?

    由于我的项目 我需要使用 c 98 和 gmpxx h 但即使对于一个简单的项目 它也不起作用 include
  • Chrome DevTools:SASS 源文件未保存在磁盘上

    我按照 Google 提供的说明设置源映射 https developers google com chrome developer tools docs css preprocessors https developers google
  • Unity错误:UnityEngine.Component'不包含“velocity”的定义

    我对 C 很陌生 所以如果这是显而易见的 请原谅我 我正在按照中的步骤操作本教程 http www instructables com id Make A 2D Infinite Runner with Unity step6 The Ro
  • 折叠表达式和函数名称查找

    我正在学习 C 17 中的折叠表达式 我有以下代码 include
  • 是否可以设置当CPU写入特定地址时中断的中断?

    当写入特定地址时是否可以使x86 cpu中断 我想要一个硬件机制来监视某些地址的变化 我想知道在写入特定地址时是否可以使x86 cpu中断 有可能的 您可以设置调试寄存器之一 DR0 to DR4 到你想要监控的地址 然后在中配置相应的标志
  • 使用 NSSortDescriptor 通过 Core Data 对内部版本号进行排序

    我正在尝试使用NSSortDescriptor使用 Core Data 对内部版本号 软件版本 NSStrings 进行排序 内部版本号实际上只是 NSString 但似乎很难做到这一点 我猜测是因为小数点有多个且不规则 例如 我想对以下内
  • scipy 优化最小化——并行化选项

    当使用 L BFGS B 方法运行 scipy Optimizeminimum 时 我发现在某些计算机上 它使用全部 8 个 cpu 核心 参见照片 1 在其他计算机上它使用 8 个核心中的 4 个 参见照片 2 而在其他计算机上 它使用
  • 如何对哈希引用切片进行求和?

    我正在尝试获取哈希引用切片的总和 但失败了 usr bin env perl use strict use warnings FATAL gt all use feature say use autodie all use List Uti
  • help() 在 Python 中哪里可以找到信息?

    我发现内置help 最近它打印了模块 函数 方法 类等的一些信息 但是它到底在哪里找到它显示的信息呢 Python 文档 https docs python org 2 library functions html help不要对此给出任何
  • 使用 guid 主键忽略 LINQ to SQL 实体列名称属性

    我正在使用 LINQ to SQL SQL Server 2005 SP3 x64 处理一个简单的实体类 Table Name TBL REGISTRATION public sealed class Registration IDataE
  • scala 范围与大型集合上的列表性能

    我对 10 000 000 个元素运行了一组性能基准测试 我发现每次实现的结果都有很大差异 任何人都可以解释为什么创建 Range ByOne 会产生比简单的基元数组更好的性能 但将相同的范围转换为列表会导致比最坏情况场景更差的性能 创建