Java 列表、基元类型和性能
我对自动装箱和性能很好奇,因为我在应用程序中做了很多速度敏感的数学运算,所以我运行了一个小测试Java 列表、基元类型和性能,java,arrays,performance,list,autoboxing,Java,Arrays,Performance,List,Autoboxing,我对自动装箱和性能很好奇,因为我在应用程序中做了很多速度敏感的数学运算,所以我运行了一个小测试 public static void main(String[] args) { // Some initialization so I know it's not involved ArrayList<Integer> list = new ArrayList<Integer>(); list.add(0); int[] regArray =
public static void main(String[] args) {
// Some initialization so I know it's not involved
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0);
int[] regArray = new int[1];
long total = 0;
// This one uses an array and primitive type
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
regArray[0] = i + 10;
if (regArray[0] % 1000 == 0) total += regArray[0];
}
System.out.println("Runtime in millis: " + (System.currentTimeMillis() - start));
System.out.println(total);
// This one autoboxes, but still uses the Object type because it's a list
total = 0;
start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
list.set(0, i + 10);
if (list.get(0) % 1000 == 0) total += list.get(0);
}
System.out.println("Runtime in millis: " + (System.currentTimeMillis() - start));
System.out.println(total);
// This one doesn't autobox
total = 0;
start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
list.set(0, new Integer(i + 10));
if (list.get(0).intValue() % 1000 == 0) total += list.get(0).intValue();
}
System.out.println("Runtime in millis: " + (System.currentTimeMillis() - start));
System.out.println(total);
}
这似乎表明我应该远离对速度敏感的mathy应用程序中的List
和子类。你同意吗,斯塔克
编辑:我的实际使用情况是,我需要存储几百个
int
s和float
s,它们会经常变化,并且在很大程度上不可预测(我说这主要是因为它们会停留在一个狭窄的范围内,但我不知道它们会在那个狭窄的范围内做什么),我需要毫秒级的响应时间来计算这些数字。编写基准是一项艰巨的任务,而您的基准不是一个好的基准,至少有以下原因:
- 在main方法中执行所有操作,而不让Hotspot进入,JIT编译您的代码会使结果出错
- 使用
而不是newinteger()
不允许使用整数缓存Integer.valueOf()
- 这些值和操作不现实。大多数情况下,值接近0,不在10000000范围内
基本体和数组通常比对象和集合快,但如果不测量实际代码,在实际条件下,很难判断使用基本体和数组所获得的收益是显著的还是完全可以忽略不计。大多数情况下,IO操作会导致性能损失。如果您有预先确定的int数,那么将它们存储在一个数组中(假设它们需要存储且不能流式传输!)通常比
java.util.ArrayList
更快,是的
但是,在许多情况下,您可能会有不同的数据大小,因此动态调整大小的集合将变得非常有用-替代方法通常是编写自己的ArrayList
实现
幸运的是,有一些基于基元类型(int等)而不是对象(Integer等)实现列表的方法。你可以看看这些。微基准标记很难!我重写了您的基准以使用:
import com.google.caliper.Runner;
导入com.google.caliper.SimpleBenchmark;
导入java.util.ArrayList;
公共类ListsBenchmark扩展了SimpleBenchmark{
private final ArrayList list=new ArrayList();
int[]regArray=新int[1];
长期总计;
@凌驾
受保护的void setUp()引发异常{
列表。添加(0);
总数=0;
}
公共长时间数组和限制类型(int代表){
对于(int i=0;i
我没有修改你原来的代码。我发现:
- 阵列速度大约快3倍
- 创建新的
稍微快一点(!),可能缓存有一些价格,也可能只是我的架构(32位Ubuntu,4核,3 GiB内存笔记本电脑)Integer
好吧,在第一个
列表中
的情况下,我让自动装箱处理它。你的意思是,如果我,比方说,循环通过同样的1000个左右的整数,它会更具可比性吗?另外,您能再解释一下Hotspot和JIT的含义吗?Hotspot是Oracle JVM的即时编译器。它分析哪些方法经常执行(热点),并将它们编译为本机代码以加快执行速度。将所有内容都放在主方法AFAIK中,使Hotspot无法完成其工作。此外,这些方法应该被调用很多时间,以使JIT将它们检测为热点,然后再次调用以测量它们的执行时间。谢谢,我从这篇文章中学到了很多,尽管它并没有真正回答我的问题。我确实在我的帖子中添加了一个用例。您的基准测试有缺陷,因为您每次都在同一个JVM调用实例中运行。因此,以后的测试有JVM预热的好处。至少,您需要在自己的JVM中运行每个测试。如果你重新安排测试顺序,你会得到不同的结果。回答得好,DNA:如果你对我的具体情况有好的建议,我会在我的问题中添加更多关于我的用例的内容。这太棒了!我觉得有动力用一个自定义的基于数组的数据缓存重写我的很多代码,并自己实现java.util.List
niceties。。。没有泛型,因为我知道现在和将来的数据类型是什么
Runtime in millis: 78
50005000000
Runtime in millis: 250
50005000000
Runtime in millis: 250
50005000000
import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;
import java.util.ArrayList;
public class ListsBenchmark extends SimpleBenchmark {
private final ArrayList<Integer> list = new ArrayList<Integer>();
int[] regArray = new int[1];
long total;
@Override
protected void setUp() throws Exception {
list.add(0);
total = 0;
}
public long timeArrayAndPrimitiveType(int reps) {
for (int i = 0; i < reps; i++) {
regArray[0] = i + 10;
if (regArray[0] % 1000 == 0)
total += regArray[0];
}
return total;
}
public long timeListWithAutoboxing(int reps) {
for (int i = 0; i < reps; i++) {
list.set(0, i + 10);
if (list.get(0) % 1000 == 0)
total += list.get(0);
}
return total;
}
public long timeNoAutoboxing(int reps) {
for (int i = 0; i < reps; i++) {
list.set(0, new Integer(i + 10));
if (list.get(0).intValue() % 1000 == 0)
total += list.get(0).intValue();
}
return total;
}
public static void main(String[] args) {
Runner.main(ListsBenchmark.class, new String[]{});
}
}