lambdas 排序_Java8 Lambdas:解释性能缺陷的排序
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秒钟才能完成测试-但是至少我们重现了该问题,并为进一步进行奠定了基础。
让我们看一下该测试的飞行记录:
可以看出有两个大问题:
lambda$comparing
方法中的性能问题- 反复调用
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秒,让我们看一下此运行的飞行记录:
当我第一次看到此消息时,我感到非常惊讶,因为到目前为止,我们还没有进行任何更改来减少对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,这是我们使用自定义比较器可以实现的。
让我们再次查看此运行的飞行记录:
现在,所有时间都在使用实际的排序方法,而不是开销。
总之,我们从这次调查中学到了一些有趣的事情:
- 由于自动装箱和虚拟表查找的成本,在某些情况下,使用新的Java8声明式排序将比编写自定义比较器慢4倍。
- FlightRecorder虽然比其他分析器要好(有关此问题,请参阅我的第一篇博客文章 ),但仍将时间归因于错误的方法,尤其是在进行内联时。
翻译自: https://www.javacodegeeks.com/2015/01/java8-lambdas-sorting-performance-pitfall-explained.html
lambdas 排序
lambdas 排序_Java8 Lambdas:解释性能缺陷的排序相关推荐
- Java8 Lambdas:解释性能缺陷的排序
与Peter Lawrey合作撰写 . 几天前,我对使用新的Java8声明式的排序性能提出了严重的问题. 在这里查看博客文章. 在那篇文章中,我仅指出了问题所在,但在这篇文章中,我将更深入地了解和解释 ...
- java steam 排序_java8 stream自定义分组求和并排序的实现
本文主要介绍了java8 stream自定义分组求和并排序的实现,分享给大家,具体如下: public static void main(String[] args) { List list = ne ...
- 惠普系列笔记本爆出严重性能缺陷
第二部分 跟中国惠普的交涉过程 从3月份开始我就在联系惠普本地客服,并多次拨打HP800客服电话,问题均未得到解决.后来,当我认识到该问题属于CPU throttle过热保护降频时,把这个词向HP的 ...
- pgpool-II的性能缺陷(二)
接上文 pgpool-II的性能缺陷: 前文已经说到,pgpool-II在replication mode状态下,是顺次而非并行执行SQL文给各个DB节点. 从Source的角度,可以看到: Simp ...
- 关于几种排序算法的时间性能比较
以前经常看到各种排序算法,今天也对以下6种排序算法的时间性能做了一次测试: 测试代码地址 1. 冒泡排序 O(n^2) /** 冒泡排序 @Param {[]int} arr 整形切片 */ func ...
- 排序算法——各算法性能
各排序算法的性能总结 最后,在这里对前面所写的八众排序进行一个算法性能总结. 到这里,八种排序算是全部写完了.
- MINA,xSocket同样的性能缺陷及陷阱,Grizzly better
2019独角兽企业重金招聘Python工程师标准>>> 本文转自博客:http://www.blogjava.net/adapterofcoms/articles/314560.ht ...
- imputation-综述文章:关于网络推理的scRNA序列插补工具基准突出了高稀疏性水平下的性能缺陷
文章题目: Benchmarking scRNA-seq imputation tools with respect to network inference highlights 中文题目: 关于网 ...
- Linux 3.10内核锁瓶颈描述以及解决-IPv6路由cache的性能缺陷
大量线程争抢锁导致CPU自旋乃至内核hang住的例子层出不穷. 我曾经解过很多关于这方面的内核bug: nat模块复制tso结构不完全导致SSL握手弹证书慢. IP路由neighbour系统对poin ...
最新文章
- 显示计算机硬盘驱动器更改,笔记本硬盘驱动器的字母怎么修改?笔记本修改硬盘驱动器字母的方法...
- 混合办公时代来了?携程试点每周两天居家办公,76%的员工主动报名!
- ASP.NET中 分析器错误:发现不明确的匹配
- 【WC2014】时空穿梭【组合数】【莫比乌斯反演】【整除分块】【暴力多项式】
- [html] js放在html的<body>和<head>有什么区别?
- windows效率工具,翻译软件QTranslate
- linux环境下装mq,ActiveMQ下载与安装(Linux环境下进行)
- 企业内部IT报修是如何操作的?
- Android移动应用基础教程【服务】
- 施乐700彩机服务器维修,施乐700DCP彩色数码复印机维修手册:700dcp_sc_ver1.1_chap05(208页)-原创力文档...
- 禾瘦美学馆,不是谁NB谁做,是谁开店谁NB
- cout输出中加入双引号
- ANSYS CFX19.0中的SA模型设置
- ggplot2读书笔记9:第六章 标度(二)
- 关于springmvc静态资源常被忽视,有可能致命的点
- SYSU-区块链原理与技术
- heart(js源码)
- 清华大学计算机王佳希,北大清华2012年拟录取保送生名单及简析(北京市)
- 深入浅出eslint——关于我学习eslint的心得
- 提高孩子的专注力就等于提高孩子的学习效率!
热门文章
- 牛客小白月赛18-记录
- POJ2018-Best Cow Fences【实数二分答案】
- P3565 [POI2014]HOT-Hotels(树形dp+长链剖分)
- 【模板】最大密度子图
- 2018 ACM ICPC Asia Regional - Seoul B.Cosmetic Survey
- codeforces1437 E. Make It Increasing——最长上升子序列
- HDU1812 - Count the Tetris
- 详解proxy_pass、upstream与resolver
- Java异常面试问题
- JAVA面试常考系列九