与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

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

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

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

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

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

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

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

  4. java8流分组 性能_Java性能教程– Java 8流有多快?

    java8流分组 性能 在此JAX Magazine的预览预览中,JAX伦敦发言人Angelika Langer为使用Java流的任何人回答了最重要的问题:它们真的更快吗? Java 8是JDK收集框 ...

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

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

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

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

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

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

  8. 性能测试之常见性能缺陷

    一.性能测试定义 性能测试,是结合被测系统应用架构.业务场景和实现细节.逻辑,对软件响应时间.处理速率.容错能力等进行分析测试,找到系统的性能瓶颈,并确认问题得到解决的过程. 二.性能测试缺陷分类 缺 ...

  9. Java8流处理正序倒序排序

    文章目录 1 问题背景 2 分析 3 实现 3.1 单字段排序 3.2 多字段级联排序 1 问题背景 场景是电商查询优惠活动的活动列表,有8种活动.针对搭售类型的活动按照状态正序.优先级倒叙来排序.其 ...

最新文章

  1. python 代码-python经典代码
  2. Redis主从复制下的工作原理
  3. python高级语法装饰器_Python高级编程——装饰器Decorator超详细讲解上
  4. mysql 免安装 自启动_MYSQL在Win下免安装zip
  5. SQLite指南(4) - FAQ列表(important)
  6. Tosca :配置环境参数
  7. 在vue文件引入echarts_vue引入echarts地图的三种方式
  8. android 按下缩小效果松开恢复_22省份三季报:粤苏总量差距缩小,19省份实现正增长...
  9. 时间序列分析工具箱——tidyquant
  10. python最小生成树算法_最小生成树:Kruskal算法及python实现
  11. 科研伦理与学术规范2021秋期末考答案|网课期末考答案|学堂在线|北京师范大学印波副教授
  12. 通用的电子商务商城后台管理界面模板(可下载)
  13. 关于学习的三个认知升级
  14. AVL树【图示详解+代码实现】
  15. 微PEU盘如何还原成普通U盘
  16. 科林明伦杯哈尔滨理工大学第六届程序设计团队赛(流水账)
  17. 蓝牙时断时续很让人恼火,该如何解决(主要针对Windows 10)
  18. 基于JAVA服装连锁店后台管理系统计算机毕业设计源码+数据库+lw文档+系统+部署
  19. 湿度传感器行业调研报告 - 市场现状分析与发展前景预测
  20. mac 终端上运行NPM INSTALL时出现问题:npm error!cb() 从未调用过!解决了

热门文章

  1. 把本地文件上传到gitee
  2. android之视频直播与播放Vitamio
  3. python scratch unity_Unity3D研究院之2D游戏开发制作原理(二十一)
  4. JAVA实现一个图片上传预览功能
  5. java 字符数组与字符串_用于字符串和数组的5种简单有效的Java技术
  6. jax-ws和jax-rs_JAX-RS和OpenAPI对Hypermedia API的支持:任重而道远
  7. aws lambda_跑来跑去:假人与AWS Lambda的第一次接触
  8. cli命令行界面 demo_Java命令行界面(第24部分):MarkUtils-CLI
  9. swarm 和 k8s_Wildfly Swarm,朝着成熟和一小部分贡献
  10. 列表流和feed流_通过流而不是列表