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秒钟才能完成测试-但是至少我们重现了该问题,并为进一步进行奠定了基础。

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

可以看出有两个大问题:

  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秒,让我们看一下此运行的飞行记录:

当我第一次看到此消息时,我感到非常惊讶,因为到目前为止,我们还没有进行任何更改来减少对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,这是我们使用自定义比较器可以实现的。

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

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

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

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

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

lambdas 排序

lambdas 排序_Java8 Lambdas:解释性能缺陷的排序相关推荐

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

    与Peter Lawrey合作撰写 . 几天前,我对使用新的Java8声明式的排序性能提出了严重的问题. 在这里查看博客文章. 在那篇文章中,我仅指出了问题所在,但在这篇文章中,我将更深入地了解和解释 ...

  2. java steam 排序_java8 stream自定义分组求和并排序的实现

    本文主要介绍了java8 stream自定义分组求和并排序的实现,分享给大家,具体如下: public static void main(String[] args) { List list = ne ...

  3. 惠普系列笔记本爆出严重性能缺陷

    第二部分  跟中国惠普的交涉过程 从3月份开始我就在联系惠普本地客服,并多次拨打HP800客服电话,问题均未得到解决.后来,当我认识到该问题属于CPU throttle过热保护降频时,把这个词向HP的 ...

  4. pgpool-II的性能缺陷(二)

    接上文 pgpool-II的性能缺陷: 前文已经说到,pgpool-II在replication mode状态下,是顺次而非并行执行SQL文给各个DB节点. 从Source的角度,可以看到: Simp ...

  5. 关于几种排序算法的时间性能比较

    以前经常看到各种排序算法,今天也对以下6种排序算法的时间性能做了一次测试: 测试代码地址 1. 冒泡排序 O(n^2) /** 冒泡排序 @Param {[]int} arr 整形切片 */ func ...

  6. 排序算法——各算法性能

    各排序算法的性能总结 最后,在这里对前面所写的八众排序进行一个算法性能总结. 到这里,八种排序算是全部写完了.

  7. MINA,xSocket同样的性能缺陷及陷阱,Grizzly better

    2019独角兽企业重金招聘Python工程师标准>>> 本文转自博客:http://www.blogjava.net/adapterofcoms/articles/314560.ht ...

  8. imputation-综述文章:关于网络推理的scRNA序列插补工具基准突出了高稀疏性水平下的性能缺陷

    文章题目: Benchmarking scRNA-seq imputation tools with respect to network inference highlights 中文题目: 关于网 ...

  9. Linux 3.10内核锁瓶颈描述以及解决-IPv6路由cache的性能缺陷

    大量线程争抢锁导致CPU自旋乃至内核hang住的例子层出不穷. 我曾经解过很多关于这方面的内核bug: nat模块复制tso结构不完全导致SSL握手弹证书慢. IP路由neighbour系统对poin ...

最新文章

  1. 显示计算机硬盘驱动器更改,笔记本硬盘驱动器的字母怎么修改?笔记本修改硬盘驱动器字母的方法...
  2. 混合办公时代来了?携程试点每周两天居家办公,76%的员工主动报名!
  3. ASP.NET中 分析器错误:发现不明确的匹配
  4. 【WC2014】时空穿梭【组合数】【莫比乌斯反演】【整除分块】【暴力多项式】
  5. [html] js放在html的<body>和<head>有什么区别?
  6. windows效率工具,翻译软件QTranslate
  7. linux环境下装mq,ActiveMQ下载与安装(Linux环境下进行)
  8. 企业内部IT报修是如何操作的?
  9. Android移动应用基础教程【服务】
  10. 施乐700彩机服务器维修,施乐700DCP彩色数码复印机维修手册:700dcp_sc_ver1.1_chap05(208页)-原创力文档...
  11. 禾瘦美学馆,不是谁NB谁做,是谁开店谁NB
  12. cout输出中加入双引号
  13. ANSYS CFX19.0中的SA模型设置
  14. ggplot2读书笔记9:第六章 标度(二)
  15. 关于springmvc静态资源常被忽视,有可能致命的点
  16. SYSU-区块链原理与技术
  17. heart(js源码)
  18. 清华大学计算机王佳希,北大清华2012年拟录取保送生名单及简析(北京市)
  19. 深入浅出eslint——关于我学习eslint的心得
  20. 提高孩子的专注力就等于提高孩子的学习效率!

热门文章

  1. 牛客小白月赛18-记录
  2. POJ2018-Best Cow Fences【实数二分答案】
  3. P3565 [POI2014]HOT-Hotels(树形dp+长链剖分)
  4. 【模板】最大密度子图
  5. 2018 ACM ICPC Asia Regional - Seoul B.Cosmetic Survey
  6. codeforces1437 E. Make It Increasing——最长上升子序列
  7. HDU1812 - Count the Tetris
  8. 详解proxy_pass、upstream与resolver
  9. Java异常面试问题
  10. JAVA面试常考系列九