Java 8性能不好,排序错误
我在codeforces上解决算法问题,并尝试使用java7和java8推出相同的解决方案,令我惊讶的是,使用java8我得到了更糟糕的解决方案 在最后一次测试中: Java 7: 时间:373毫秒,内存:112 KB java 8: 时间:623毫秒,内存:3664 KB 我的代码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();
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*100000Integer
对象只需要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