Java 8性能不好,排序错误

Java 8性能不好,排序错误,java,Java,我在codeforces上解决算法问题,并尝试使用java7和java8推出相同的解决方案,令我惊讶的是,使用java8我得到了更糟糕的解决方案 在最后一次测试中: Java 7: 时间:373毫秒,内存:112 KB java 8: 时间:623毫秒,内存:3664 KB 我的代码 public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt();

我在codeforces上解决算法问题,并尝试使用java7和java8推出相同的解决方案,令我惊讶的是,使用java8我得到了更糟糕的解决方案

在最后一次测试中: Java 7: 时间:373毫秒,内存:112 KB

java 8: 时间:623毫秒,内存:3664 KB

我的代码

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    int n = in.nextInt();
    int m = in.nextInt();
    List<Integer> list1 = new ArrayList<>(n);
    List<Integer> list2 = new ArrayList<>(m);
    for(int i=0;i<n;i++) {
        list1.add(in.nextInt());
    }
    for (int i = 0; i < m; i++) {
        list2.add(in.nextInt());
    }

    Collections.sort(list1);
    Collections.sort(list2, Collections.reverseOrder());

    int max = Math.min(list1.size(), list2.size());
    int a,b;
    long result = 0;
    for(int i=0;i<max;i++) {
        a = list1.get(i);
        b = list2.get(i);
        if(b > a) result += ( b- a);
        else break;
    }

    System.out.println(result);


}
publicstaticvoidmain(字符串[]args){
扫描仪输入=新扫描仪(系统输入);
int n=in.nextInt();
int m=in.nextInt();
列表列表1=新的ArrayList(n);
列表列表2=新的ArrayList(m);

对于(inti=0;i可能有几个原因,但我的第一个猜测是Java8附带了许多附加功能,因此初始加载时间更大

如果我们假设
n
不是很大,可以说处理时间可以忽略不计

您使用哪种数字顺序(
n
m
)执行程序

您还可以使用
stdin
提供数字。我假设您使用某种管道?否则输入值当然是处理时间的一部分


为了更有效地对此进行基准测试,您需要在同一次运行中多次重复您的实验……只有这样,处理时间才成为主要因素。

关于这个问题,有许多问题尚不清楚。这些都是显而易见的事情,比如您如何运行计时测试,如何提供输入数字,以及输入数字的大小

此外,当您说在Java 7中存储2*100000
Integer
对象只需要112KB时,那么一定会涉及到一些神奇的压缩,因为数据本身已经需要至少800KB,忽略了任何进一步的对象开销。此外,排序甚至不应该需要373ms与您提到的数字,而是在现代PC上可能只有50毫秒

然而,在我将在这里收集的评论中有一些提示,希望它会被认为是有用的(甚至可能会回答你的问题)


首先,微标杆是一门艺术,要想获得“可靠”,还有很多事情要考虑。结果,您必须使用微基准标记工具包,如或。但要快速获得结果的大致近似值,您至少应重复实际测试几次。对于您的情况,大致可以这样做:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class SortingSpeedTest {

    private static List<Integer> createList(int n, int offset, int range) {
        Random random = new Random(0);
        List<Integer> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            list.add(random.nextInt(range) + offset);
        }
        return list;
    }

    public static void main(String[] args) {
        for (int m = 1000; m <= 1000000; m *= 10) {
            for (int n = 1000; n <= 1000000; n *= 10) {

                runTest(m, n);
            }
        }

    }

    private static void runTest(int m, int n) {
        List<Integer> list1 = createList(m, 0, 100);
        List<Integer> list2 = createList(n, 100, 100);

        long before = System.nanoTime();
        Collections.sort(list1);
        Collections.sort(list2, Collections.reverseOrder());
        int max = Math.min(list1.size(), list2.size());
        int a, b;
        long result = 0;
        for (int i = 0; i < max; i++) {
            a = list1.get(i);
            b = list2.get(i);
            if (b > a)
                result += (b - a);
            else
                break;
        }
        long after = System.nanoTime();
        System.out.printf("m=%7d n=%7d result=%14d duration %8.3f\n", m, n,
                result, (after - before) / 1e6);
    }
}
Java 8中的这些结果:

m=   1000 n=   1000 result=        100000 duration    4,271
m=   1000 n=  10000 result=        144379 duration   10,414
m=   1000 n= 100000 result=        149303 duration   45,413
m=   1000 n=1000000 result=        149336 duration  179,397
m=  10000 n=   1000 result=        145269 duration    7,422
m=  10000 n=  10000 result=       1000000 duration    6,464
m=  10000 n= 100000 result=       1450247 duration   54,259
m=  10000 n=1000000 result=       1494798 duration  158,869
m= 100000 n=   1000 result=        149664 duration   17,863
m= 100000 n=  10000 result=       1449668 duration   16,904
m= 100000 n= 100000 result=      10000000 duration   33,613
m= 100000 n=1000000 result=      14490326 duration  158,988
m=1000000 n=   1000 result=        149664 duration  133,977
m=1000000 n=  10000 result=       1495165 duration  139,210
m=1000000 n= 100000 result=      14510529 duration  156,483
m=1000000 n=1000000 result=     100000000 duration 1080,306
您可以看到,除最后一个结果外,结果大致相同。再次启动它,并另外指定
-verbose:gc
显示原因:

...
m=1000000 n= 100000 result=      14510529 duration  156,934
...
[Full GC (Ergonomics)  64237K->24095K(79360K), 0.7443466 secs]
m=1000000 n=1000000 result=     100000000 duration 1056,624
它在最后一次通过之前执行完整的垃圾收集(在使用上述基准测试框架时可以避免类似的情况!)

现在可以开始一些GC调优了,关于这方面的文章很多很多(这不仅仅是一门艺术,这是一门黑色艺术)。但是GC的实际原因可以从程序中得到:它正在创建大量必须清理的
Integer
对象

根据这些列表中
Integer
对象的大小,可以避免这种情况。从
int
Integer
的自动装箱在内部依赖于“small”的缓存
int
值。此缓存的默认大小相当小。但是当以
-XX:AutoBoxCacheMax=300
启动上述程序时,几乎所有GC暂停都将消失,因为在此测试中没有大于300的
整数值


旁白:这里是对程序的简单修改,以使用并行流

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

public class ParallelSortingSpeedTest {

    private static List<Integer> createList(int n, int offset, int range) {
        Random random = new Random(0);
        List<Integer> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            list.add(random.nextInt(range) + offset);
        }
        return list;
    }

    public static void main(String[] args) {
        for (int m = 1000; m <= 1000000; m *= 10) {
            for (int n = 1000; n <= 1000000; n *= 10) {

                runTest(m, n);
            }
        }

    }

    private static void runTest(int m, int n) {
        List<Integer> list1 = createList(m, 0, 100);
        List<Integer> list2 = createList(n, 100, 100);

        long before = System.nanoTime();
        list1 = list1.parallelStream().sorted().collect(Collectors.toList());
        list2 = list2.parallelStream().sorted(
            Collections.reverseOrder()).collect(Collectors.toList());
        int max = Math.min(list1.size(), list2.size());
        int a, b;
        long result = 0;
        for (int i = 0; i < max; i++) {
            a = list1.get(i);
            b = list2.get(i);
            if (b > a)
                result += (b - a);
            else
                break;
        }
        long after = System.nanoTime();
        System.out.printf("m=%7d n=%7d result=%14d duration %8.3f\n", m, n,
                result, (after - before) / 1e6);
    }
}

怀疑启动开销或可能的自动装箱骗局。单独在
main
中运行时钟以消除第一种可能性。您是否一直得到这些结果?我再运行一次,结果类似我怀疑是垃圾回收。您的IO很可能会影响您的计时。您需要反复测试,例如r 10秒,不包括加载数据的时间。您还应该分别测试两个列表。可能是一个比较慢,另一个不慢。此外,如果您关心速度,我建议使用
int[]
而不是ArrayList。如果你不关心速度,我也不会担心。考虑到列表必须手动填写,可能是小n和m@LionC输入也可以通过管道从文本文件传输。我不明白“数字的顺序是什么…”这是这个问题的解决方案,有n和m的范围。在上一次测试中,n和m可能是100000,但如果是这样的话,我想他会在他的短代码示例中为我们使用文件。无论如何,只有OP可以澄清:-)@Dwq:well 100000实际上不是一个重要的数字。排序是O(n log n)(理论上是O(n^2)),但如果
n
的大小为
100000
,则需要几毫秒的处理时间。因此
x00
毫秒的差异肯定有另一个原因。。。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

public class ParallelSortingSpeedTest {

    private static List<Integer> createList(int n, int offset, int range) {
        Random random = new Random(0);
        List<Integer> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            list.add(random.nextInt(range) + offset);
        }
        return list;
    }

    public static void main(String[] args) {
        for (int m = 1000; m <= 1000000; m *= 10) {
            for (int n = 1000; n <= 1000000; n *= 10) {

                runTest(m, n);
            }
        }

    }

    private static void runTest(int m, int n) {
        List<Integer> list1 = createList(m, 0, 100);
        List<Integer> list2 = createList(n, 100, 100);

        long before = System.nanoTime();
        list1 = list1.parallelStream().sorted().collect(Collectors.toList());
        list2 = list2.parallelStream().sorted(
            Collections.reverseOrder()).collect(Collectors.toList());
        int max = Math.min(list1.size(), list2.size());
        int a, b;
        long result = 0;
        for (int i = 0; i < max; i++) {
            a = list1.get(i);
            b = list2.get(i);
            if (b > a)
                result += (b - a);
            else
                break;
        }
        long after = System.nanoTime();
        System.out.printf("m=%7d n=%7d result=%14d duration %8.3f\n", m, n,
                result, (after - before) / 1e6);
    }
}
....
m=1000000 n=  10000 result=       1495165 duration   95,386
m=1000000 n= 100000 result=      14510529 duration   95,467
m=1000000 n=1000000 result=     100000000 duration  172,782