欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > java >内容正文

java

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

发布时间:2023/12/3 java 48 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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秒钟才能完成测试-但是至少我们重现了该问题,并为进一步进行奠定了基础。

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

可以看出有两个大问题:

  • 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

    总结

    以上是生活随笔为你收集整理的Java8 Lambdas:解释性能缺陷的排序的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。