Disclaimer: I have looked through this question and this question but they both got derailed by small details and general optimization-is-unnecessary concerns. I really need all the performance I can get in my current app, which is receiving-processing-spewing MIDI data in realtime. Also it needs to scale up as well as possible.
我正在比较array
小列表的大量读取性能到 ArrayList
并且还只是掌握了变量。我发现数组胜过 ArrayList
2.5 倍,甚至胜过仅拥有对象引用。
我想知道的是:
- 我的基准测试还好吗? 我已经改变了测试顺序和运行次数,没有任何变化。我也使用毫秒而不是纳秒来无济于事。
- 我是否应该指定任何 Java 选项来最小化这种差异?
- 如果这种差异是真实的,在这种情况下我不应该更喜欢
Test[]
吗?至ArrayList<Test>
在这种情况下并放入转换它们所需的代码?显然我读的比写的多得多。
JVM 是 OSX 上的 Java 1.6.0_17,它肯定是在 Hotspot 模式下运行。
public class ArraysVsLists {
static int RUNS = 100000;
public static void main(String[] args) {
long t1;
long t2;
Test test1 = new Test();
test1.thing = (int)Math.round(100*Math.random());
Test test2 = new Test();
test2.thing = (int)Math.round(100*Math.random());
t1 = System.nanoTime();
for (int i=0; i<RUNS; i++) {
test1.changeThing(i);
test2.changeThing(i);
}
t2 = System.nanoTime();
System.out.println((t2-t1) + " How long NO collection");
ArrayList<Test> list = new ArrayList<Test>(1);
list.add(test1);
list.add(test2);
// tried this too: helps a tiny tiny bit
list.trimToSize();
t1= System.nanoTime();
for (int i=0; i<RUNS; i++) {
for (Test eachTest : list) {
eachTest.changeThing(i);
}
}
t2 = System.nanoTime();
System.out.println((t2-t1) + " How long collection");
Test[] array = new Test[2];
list.toArray(array);
t1= System.nanoTime();
for (int i=0; i<RUNS; i++) {
for (Test test : array) {
test.changeThing(i);
}
}
t2 = System.nanoTime();
System.out.println((t2-t1) + " How long array ");
}
}
class Test {
int thing;
int thing2;
public void changeThing(int addThis) {
thing2 = addThis + thing;
}
}
最佳答案
微基准测试很难在像 Java 这样的平台上正确执行。您肯定必须将要进行基准测试的代码提取到单独的方法中,将它们作为预热运行几千次,然后进行测量。我已经这样做了(下面的代码),结果是通过引用直接访问的速度是通过数组的三倍,但集合仍然慢了 2 倍。
这些数字基于 JVM 选项 -server -XX:+DoEscapeAnalysis
。如果没有 -server
,使用集合的速度将急剧变慢(但奇怪的是,直接访问和数组访问要快很多,这表明发生了一些奇怪的事情)。 -XX:+DoEscapeAnalysis
为收集产生了另外 30% 的加速,但它是否适用于您的实际生产代码非常值得怀疑。
总的来说,我的结论是:忘记微基准测试吧,它们很容易产生误导。尽可能接近生产代码进行测量,而无需重写整个应用程序。
import java.util.ArrayList;
public class ArrayTest {
static int RUNS_INNER = 1000;
static int RUNS_WARMUP = 10000;
static int RUNS_OUTER = 100000;
public static void main(String[] args) {
long t1;
long t2;
Test test1 = new Test();
test1.thing = (int)Math.round(100*Math.random());
Test test2 = new Test();
test2.thing = (int)Math.round(100*Math.random());
for(int i=0; i<RUNS_WARMUP; i++)
{
testRefs(test1, test2);
}
t1 = System.nanoTime();
for(int i=0; i<RUNS_OUTER; i++)
{
testRefs(test1, test2);
}
t2 = System.nanoTime();
System.out.println((t2-t1)/1000000.0 + " How long NO collection");
ArrayList<Test> list = new ArrayList<Test>(1);
list.add(test1);
list.add(test2);
// tried this too: helps a tiny tiny bit
list.trimToSize();
for(int i=0; i<RUNS_WARMUP; i++)
{
testColl(list);
}
t1= System.nanoTime();
for(int i=0; i<RUNS_OUTER; i++)
{
testColl(list);
}
t2 = System.nanoTime();
System.out.println((t2-t1)/1000000.0 + " How long collection");
Test[] array = new Test[2];
list.toArray(array);
for(int i=0; i<RUNS_WARMUP; i++)
{
testArr(array);
}
t1= System.nanoTime();
for(int i=0; i<RUNS_OUTER; i++)
{
testArr(array);
}
t2 = System.nanoTime();
System.out.println((t2-t1)/1000000.0 + " How long array ");
}
private static void testArr(Test[] array)
{
for (int i=0; i<RUNS_INNER; i++) {
for (Test test : array) {
test.changeThing(i);
}
}
}
private static void testColl(ArrayList<Test> list)
{
for (int i=0; i<RUNS_INNER; i++) {
for (Test eachTest : list) {
eachTest.changeThing(i);
}
}
}
private static void testRefs(Test test1, Test test2)
{
for (int i=0; i<RUNS_INNER; i++) {
test1.changeThing(i);
test2.changeThing(i);
}
}
}
class Test {
int thing;
int thing2;
public void changeThing(int addThis) {
thing2 = addThis + thing;
}
}
关于java - 在 Java : Is my benchmarking code wrong? 中对小型数组与列表进行基准测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2227947/