Java8 Lambdas:解释性能缺陷的排序

与Peter Lawrey合作撰写 。

几天前,我对使用新的Java8声明式的排序性能提出了严重的问题。 在这里查看博客文章。 在那篇文章中,我仅指出了问题所在,但在这篇文章中,我将更深入地了解和解释问题的原因。 这将通过使用声明式样式重现问题,然后一点一点地修改代码来完成,直到我们消除了性能问题并保留了使用旧样式比较所期望的性能。

回顾一下,我们对此类的实例进行排序:

private static class MyComparableInt{private int a,b,c,d;public MyComparableInt(int i) {a = i%2;b = i%10;c = i%1000;d = i;}public int getA() return a;public int getB() return b;public int getC() return c;public int getD() return d;
}

使用声明性的Java 8样式(如下),大约需要6秒钟才能排序10m个实例:

List mySortedList = myComparableList.stream().sorted(Comparator.comparing(MyComparableInt::getA).thenComparing(MyComparableInt::getB).thenComparing(MyComparableInt::getC).thenComparing(MyComparableInt::getD)).collect(Collectors.toList());

使用自定义排序器(如下)花费了约1.6秒的时间来排序10m个实例。

这是排序的代码调用:

List mySortedList = myComparableList.stream().sorted(MyComparableIntSorter.INSTANCE).collect(Collectors.toList());

使用此自定义比较器:

public enum MyComparableIntSorter implements Comparator<MyComparableInt>{INSTANCE;@Overridepublic int compare(MyComparableInt o1, MyComparableInt o2) {int comp = Integer.compare(o1.getA(), o2.getA());if(comp==0){comp = Integer.compare(o1.getB(), o2.getB());if(comp==0){comp = Integer.compare(o1.getC(), o2.getC());if(comp==0){comp = Integer.compare(o1.getD(), o2.getD());}}}return comp;}}

让我们在类中创建一个comparing方法,以便我们可以更紧密地分析代码。 comparing方法的原因是允许我们轻松交换实现,但将调用代码保持不变。

在所有情况下,这都是comparing方法的调用方式:

List mySortedList = myComparableList.stream().sorted(comparing(MyComparableInt::getA,MyComparableInt::getB,MyComparableInt::getC,MyComparableInt::getD)).collect(Collectors.toList());

比较的第一个实现几乎是jdk中的一个副本。

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T, ? extends U> ke1,Function<? super T, ? extends U> ke2,Function<? super T, ? extends U> ke3,Function<? super T, ? extends U> ke4){return  Comparator.comparing(ke1).thenComparing(ke2).thenComparing(ke3).thenComparing(ke4);}

毫不奇怪,这花了大约6秒钟才能完成测试-但是至少我们重现了该问题,并为进一步进行奠定了基础。

让我们看一下该测试的飞行记录:

屏幕截图2015年1月22日的11.56.20

可以看出有两个大问题:

  1. lambda$comparing方法中的性能问题
  2. 反复调用Integer.valueOf (自动装箱)

让我们尝试处理比较方法中的第一个方法。 乍一看,这似乎很奇怪,因为当您查看代码时,该方法没有发生太多事情。 然而,随着代码找到该函数的正确实现,虚拟表查找将在这里广泛进行。 当从一行代码中调用多种方法时,将使用虚拟表查找。 我们可以通过下面的comparing实现消除这种延迟源。 通过扩展Function接口的所有用途,每一行只能调用一个实现,因此可以内联该方法。

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T, ? extends U> ke1,Function<? super T, ? extends U> ke2,Function<? super T, ? extends U> ke3,Function<? super T, ? extends U> ke4){return  (c1, c2) -> {int comp = compare(ke1.apply(c1), ke1.apply(c2));if (comp == 0) {comp = compare(ke2.apply(c1), ke2.apply(c2));if (comp == 0) {comp = compare(ke3.apply(c1), ke3.apply(c2));if (comp == 0) {comp = compare(ke4.apply(c1), ke4.apply(c2));}}}return comp;};}

通过展开方法,JIT应该能够内联方法查找。

确实,时间几乎减少了一半至3.5秒,让我们来看一下此运行的飞行记录:

屏幕截图2015年1月22日的11.57.58

当我第一次看到此消息时,我感到非常惊讶,因为到目前为止,我们还没有进行任何更改来减少对Integer.valueOf的调用,但是该百分比已经下降了! 发生了什么实际上发生在这里的是,由于变化,我们提出允许内联,该Integer.valueOf已内联,并采取了时间Integer.valueOf被呼叫者指责( lambda$comparing ),它内联了被调用者( Integer.valueOf )。 这是事件探查器中的一个常见问题,因为他们可能会误解应归咎于哪种方法,尤其是在内联发生时。

但是我们知道在之前的Flight Recording Integer.valueOf
突出显示了,因此让我们通过comparing实现comparing删除,看看是否可以进一步减少时间。

return  (c1, c2) -> {int comp = compare(ke1.applyAsInt(c1), ke1.applyAsInt(c2));if (comp == 0) {comp = compare(ke2.applyAsInt(c1), ke2.applyAsInt(c2));if (comp == 0) {comp = compare(ke3.applyAsInt(c1), ke3.applyAsInt(c2));if (comp == 0) {comp = compare(ke4.applyAsInt(c1), ke4.applyAsInt(c2));}}}return comp;
};

使用此实现,时间可以缩短到1.6s,这是我们使用自定义比较器可以实现的。

让我们再次查看此运行的飞行记录:

屏幕截图于2015年1月22日的12.59.27

现在,所有时间都在使用实际的排序方法,而不是开销。

总之,我们从这次调查中学到了一些有趣的事情:

  1. 由于自动装箱和虚拟表查找的成本,在某些情况下,使用新的Java8声明式排序将比编写自定义比较器慢4倍。
  2. FlightRecorder虽然比其他分析器要好(有关此问题,请参阅我的第一篇博客文章 ),但仍将时间归因于错误的方法,尤其是在进行内联时。

翻译自: https://www.javacodegeeks.com/2015/01/java8-lambdas-sorting-performance-pitfall-explained.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361034.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Asp.net MVC3.0 基于不同的角色显示不同的菜单

前面提到过用Asp.net MVC3.0正在做一个问答系统性质的论坛。前期把菜单全部显示以方便测试模块功能。现在正在完善&#xff0c;加上角色模块&#xff0c;然后不同的角色登陆系统会看到不同的菜单栏&#xff0c;还有就是游客&#xff08;未登录用户&#xff09;看到的菜单栏。网…

LoadRunner如何监控Linux下的系统资源

前一段时间在研究LoadRunner过程中&#xff0c;在进行压力场景测试中通过LoadRunner来实时监控windows的系统资源&#xff0c;在前几节中我已经总结了相关过程&#xff0c;近段时间发现群里有朋友问如何监控Linux下的系统资源&#xff0c;所以我也就此问题搭建了一些的Linux环境…

页面跳转多种方法(加传参)

onclick"javascript:location.href/HelpCenter/HelpCenter/" <a href"/HelpCenter/HelpCenter">帮助中心</a>点击页面返回上一页&#xff1a; onclick"javascript:window.history.go(-1); *********************************************…

JCG学院开设了Java设计模式课程!

自从我们推出JCG学院以来&#xff0c;已经有一段时间了。JCG学院是一个基于付费内容的高级订阅网站&#xff0c;提供有关最新技术的课程&#xff0c;涵盖从RedSQL数据库&#xff08;如Redis和CouchDB&#xff09;到使用Android进行移动开发的最新知识。 当然&#xff0c;与Jav…

用友异常清理工具

此类工具网上很多&#xff0c;但&#xff0c;网上的病毒千千万万&#xff0c;还是自己开发使用较为放心。而且具体执行了什么也一清二楚&#xff0c;可以放心。 此工具适用大部份版本&#xff0c;从U821至U871&#xff0c;包括U6系列。 转载于:https://www.cnblogs.com/wuxi15/…

JVM因“ OutOfMemory”错误而关闭-我该怎么办?

看起来似乎很神奇&#xff0c;但是在有关JVM设置的搜索请求结果中经常显示这种“从深度”的呼喊。 您可能会遇到“我记得该选项&#xff0c;但如何启用它”的问题&#xff0c;而有时&#xff08;主要是半年一次&#xff09;管理服务器或调整虚拟设备&#xff0c;而又除主要任务…

JBoss Data Virtualization 6.1 Beta现在可用

JBoss 数据虚拟化 &#xff08;JDV&#xff09;是一种数据集成解决方案&#xff0c;位于多个数据源的前面&#xff0c;并允许将它们视为一个源。 做到这一点&#xff0c;它提供了数据抽象&#xff0c;联合&#xff0c;集成&#xff0c;转换和交付功能&#xff0c;可将来自一个或…

点击显示底框颜色,默认显示第一个。

页面初始化显示第一个底框颜色&#xff0c;点击另一个第一个底框颜色消失&#xff0c;被点击的底框颜色显示&#xff0c;以此循环。 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional…

在三个Java IDE中生成的三种常见方法

在本文中&#xff0c;我研究了NetBeans 8.0.2 &#xff0c; IntelliJ IDEA 14.0.2和Eclipse Luna 4.4.1生成的三种“通用”方法[ equals&#xff08;Object&#xff09; &#xff0c; hashCode&#xff08;&#xff09;和toString&#xff08;&#xff09; ]的区别 。 。 目的不…

angularjs 利用filter进行表单查询及分页查询

页面&#xff1a; <div><input style"width:90%;margin-left:5px;margin-right:5px;" class"form-control sys_input" ng-model"imagePaths.filter.imageName" placeholder"查询..."/></div><div><!--<…

为什么现在是升级到Java 8的最佳时机

有兴趣了解如何通过AppDynamics充分利用Java 8的新功能吗&#xff1f; 立即开始免费试用 &#xff01; 今年3月&#xff0c;Oracle发布了近十年来最受期待的版本Java8。自发布以来&#xff0c;最新版本引起了越来越多的关注&#xff0c;各种规模的公司都渴望升级。 我们的合作…

requirejs与anjularjs框架

1.目录 2.首页login.html如下&#xff1a; <!DOCTYPE html><html> <head> <title>登录界面</title> <link relstylesheet href/stylesheets/style.css /> <link rel"stylesheet" href"/css/bootstrap.min.css">…

图片循环播放

使用 pageSwitch插件 多种效果 引入 jquery.js 和 pageSwitch.min.js <script src"js/jquery-1.11.0.min.js"></script> <script src"dist/pageSwitch.min.js"></script>在页面定义标签 <div id"container">…

SSL / TLS REST服务器–带有Spring和TomEE的客户端

在构建系统时&#xff0c;开发人员通常会忽略安全性方面。 安全一直是令人担忧的重要问题&#xff0c;但是它比以前吸引了更高的关注。 就在今年&#xff0c;我们发生了像Heartbleed Bug或CelebrityGate丑闻这样的案件。 这与帖子无关&#xff0c;只是安全真正重要的示例&#…

使用Spring Boot和Logback登录到Redis

在进行集中式日志记录时&#xff0c;例如使用Elasticsearch&#xff0c;Logstash和Kibana或Graylog2&#xff0c;您可以为Java应用程序提供几个选项。 您既可以编写标准的应用程序日志&#xff0c;也可以使用Logstash解析这些日志&#xff0c;这些日志既可以直接使用&#xff0…

(1)《Head First HTML与CSS》学习笔记---HTML基本概念

前言&#xff1a; 1. 这本书并没有面面俱到&#xff0c;涵盖所有内容&#xff0c;只提供作为初学者真正需要的东西&#xff1a;基本知识和信心。所以这不是唯一的参考书。&#xff08;我买了一本《HTML5权威指南》作为参考书和这本一起看&#xff0c;但还是以本书为第一个…

与Java EE和Camel的轻量级集成

Enterprise Java具有不同的风格和观点。 从简单的平台技术开始&#xff0c;即众所周知的Java EE&#xff0c;再到不同的框架和集成方面&#xff0c;最后是涉及以数据为中心的用户界面或特定可视化效果的用例。 Java EE本身无法解决的最突出的问题是“集成”。 有许多来自知名供…

[工具库]JFileDownloader工具类——多线程下载网络文件,并保存在本地

本人大四即将毕业的准程序员&#xff08;JavaSE、JavaEE、android等&#xff09;一枚&#xff0c;小项目也做过一点&#xff0c;于是乎一时兴起就写了一些工具。 我会在本博客中陆续发布一些平时可能会用到的工具。 代码质量可能不是很好&#xff0c;大家多担待&#xff01; 代…

使用Bean验证扩展PrimeFaces CSV

你们中有些人已经知道我和我的合著者Mertalışkan正在研究PrimeFaces Cookbook的2.版。 Packt Publishing允许我从新章节“客户端验证”的一个食谱中摘录一小部分摘录。 这将有助于使读者知道这本书的内容。 在此博客文章中&#xff0c;我想讨论使用Bean验证扩展的PrimeFaces客…

ASP.NET.CORE发布后启动网站出现500.19-0x8007000d错误解决方法

本项目使用的是netcoreapp2.2&#xff0c;缺少的XML文件是swagger。发布采用的是文件系统、依赖框架。 我第一次发布asp.net.core的后台&#xff0c;发布后启动网站出现500.19错误-0x8007000d。百度查了一下原因&#xff0c;2其中大多数人说是因为没有权限&#xff0c;需要编辑…