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,一经查实,立即删除!

相关文章

jquery子元素选择器

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns"http://www.w3.org/1999/xhtml"><head><title>子元素选择器</title>…

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

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

Apache Mesos:编写您自己的分布式框架

在上一篇文章中 &#xff0c;我们了解了mesos是什么&#xff0c;它如何有用&#xff0c;并开始使用它。 在本文中&#xff0c;我们将看到如何在mesos上编写自己的框架。 &#xff08;在mesos中&#xff0c;框架是在其上运行的任何应用程序。&#xff09;本文介绍了一个名为“ m…

jquery可见性选择器(综合)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns"http://www.w3.org/1999/xhtml"><head><title>可见性选择器</title>…

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); *********************************************…

log4j的使用

文章出处&#xff1a;http://www.blogjava.net/zJun/archive/2006/06/28/55511.html 首先&#xff0c;要在项目classpath下建立log4j.properties 1. 配置文件 #配置根Logger log4j.rootLogger [level], appenderName1, appenderName2, …#配置日志信息输出目的地Appender log4…

使用@Rule在JUnit中测试文件和目录

多亏了TemporaryFolder Rule在JUnit中使用文件和目录进行测试很容易。 在JUnit中&#xff0c;规则&#xff08; Rule &#xff09;可以替代或设置夹具设置和清除方法&#xff08; org.junit.Before &#xff0c; org.junit.After &#xff0c; org.junit.BeforeClass和org.jun…

jquery简单原则器(匹配索引为指定值的元素)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns"http://www.w3.org/1999/xhtml"><head><title>简单选择器</title>&l…

POJ-1664 放苹果 动态规划思想解组合数学

该题说明了状态开设的意义一样,但是从哪个方向去理解推倒状态的转移对解题非常关键.该题扣住是否所有的盘子中有空盘子,就得到了一个非常简单且优美的方程.如果从当前盘子的放置状态或者是当前苹果的放置状态来求解状态转移方程就不能写出来.这和题意中的相同盘子,相同苹果有很…

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;而又除主要任务…

jquery层级原则器(匹配后代元素div)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns"http://www.w3.org/1999/xhtml"><head><title>层级 </title><met…

输入焦点默认指示在编辑框上

mfc 对话框(模态)中有一编辑框控件 如何在显示对话框时 将输入焦点默认指示在编辑框上呢&#xff1f; 2011-05-11 11:40 提问者&#xff1a; 绝岭雄风 我来帮他解答 满意回答2011-05-11 11:42对话框上按ctrlD 出来后先点那个编辑框控件 使得那个空间的排序为1号即可 追问太感谢…

JBoss Data Virtualization 6.1 Beta现在可用

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

hibernater面试一

什么是Hibernate的并发机制&#xff1f;怎么去处理并发问题&#xff1f; Hibernate并发机制&#xff1a; a、Hibernate的Session对象是非线程安全的,对于单个请求,单个会话,单个的工作单元(即单个事务,单个线程),它通常只使用一次, 然后就丢弃。 如果一个Session 实例允许共享的…

使用Spring Boot和MongoDB创建REST API

Spring Boot是一个自以为是的框架&#xff0c;可简化Spring应用程序的开发。 它使我们摆脱了复杂配置文件的束缚&#xff0c;并帮助我们创建了不需要外部servlet容器的独立Spring应用程序。 这听起来实在令人难以置信&#xff0c;但Spring Boot确实可以完成所有这一切 。 这篇…

Oracle用 odp.net 时出现 Oracle.DataAccess.Client.OracleConnection的类型初始值设定项引发异常 问题的解决...

在初次使用 Oracle 时&#xff0c;用ado.net连接数据&#xff0c;出现 “Oracle.DataAccess.Client.OracleConnection”的类型初始值设定项引发异常 的提示&#xff1a; 解决方法&#xff1a;将对应版本的 OraOps11W.dll 拷到应用程序目录。 转载于:https://www.cnblogs.com/pp…

如何将Jersey集成到Spring MVC应用程序中

我最近开始使用Java为Podcastpedia.org和JAX-RS实现构建公共的REST API&#xff0c;我选择了Jersey &#xff0c;因为我发现它“自然”且功能强大-您可以通过遵循教程了解更多信息-带有Jersey和Spring的Java中的REST API设计和实现。 由于Podcastpedia.org是由Spring MVC支持的…