13

看看下面的代码:

class Test
{
    public static void main(String abc[])
    {
        for( int N=1; N <= 1_000_000_000; N=N*10)
        {
            long t1 = System.nanoTime();

            start(N);

            long t2 = System.nanoTime() - t1;
            System.out.println("Time taken for " + N + " : " + t2);
        }
    }

    public static void start( int N )
    {
        int j=1;
        for(int i=0; i<=N; i++)
            j=j*i;
    }
}

上述问题产生的输出是:

Time taken for 1 : 7267
Time taken for 10 : 3312
Time taken for 100 : 7908
Time taken for 1000 : 51181
Time taken for 10000 : 432124
Time taken for 100000 : 4313696
Time taken for 1000000 : 9347132
Time taken for 10000000 : 858
Time taken for 100000000 : 658
Time taken for 1000000000 : 750

问题:

1.) 为什么 N=1 所花费的时间异常大于 N=10 ?(有时甚至超过 N=100)

2.) 为什么 N=10M 及以后的时间异常低?

上述问题中指出的模式是深刻的,即使经过多次迭代仍然存在。这里与记忆有什么联系吗?

编辑:

谢谢您的回答。我想用实际的循环替换方法调用。但是现在,没有 JIT 优化。为什么不 ?是否将语句放入有助于优化过程的方法中?修改后的代码如下:

class test
{
    public static void main(String abc[])
    {
        for( int k=1; k<=3; k++)
        {
            for( int N=1; N<=1_000_000_000; N=N*10)
            {
                long t1 = System.nanoTime();

                int j=1;
                for(int i=0; i<=N; i++)
                    j=j*i;

                long t2 = System.nanoTime() - t1;
                System.out.println("Time taken for "+ N + " : "+ t2);
            }
        }
    }
}

编辑2: 上述修改代码的输出:

Time taken for 1 : 2160
Time taken for 10 : 1142
Time taken for 100 : 2651
Time taken for 1000 : 19453
Time taken for 10000 : 407754
Time taken for 100000 : 4648124
Time taken for 1000000 : 12859417
Time taken for 10000000 : 13706643
Time taken for 100000000 : 136928177
Time taken for 1000000000 : 1368847843
Time taken for 1 : 264
Time taken for 10 : 233
Time taken for 100 : 332
Time taken for 1000 : 1562
Time taken for 10000 : 17341
Time taken for 100000 : 136869
Time taken for 1000000 : 1366934
Time taken for 10000000 : 13689017
Time taken for 100000000 : 136887869
Time taken for 1000000000 : 1368178175
Time taken for 1 : 231
Time taken for 10 : 242
Time taken for 100 : 328
Time taken for 1000 : 1551
Time taken for 10000 : 13854
Time taken for 100000 : 136850
Time taken for 1000000 : 1366919
Time taken for 10000000 : 13692465
Time taken for 100000000 : 136833634
Time taken for 1000000000 : 1368862705
4

3 回答 3

16

1.) 为什么 N=1 所花费的时间异常大于 N=10

因为这是虚拟机第一次看到该代码——它可能决定只解释它,或者将它 JIT 到本机代码需要一点时间,但可能没有优化。这是对 Java 进行基准测试的“陷阱”之一。

2.) 为什么 N=10M 及以后的时间异常低?

那时,JIT 更加努力地优化代码——将其减少到几乎为零。

特别是,如果您多次运行此代码(只是在循环中),您将看到 JIT 编译器优化的效果:

Time taken for 1 : 3732
Time taken for 10 : 1399
Time taken for 100 : 3266
Time taken for 1000 : 26591
Time taken for 10000 : 278508
Time taken for 100000 : 2496773
Time taken for 1000000 : 4745361
Time taken for 10000000 : 933
Time taken for 100000000 : 466
Time taken for 1000000000 : 933
Time taken for 1 : 933
Time taken for 10 : 467
Time taken for 100 : 466
Time taken for 1000 : 466
Time taken for 10000 : 933
Time taken for 100000 : 466
Time taken for 1000000 : 933
Time taken for 10000000 : 467
Time taken for 100000000 : 467
Time taken for 1000000000 : 466
Time taken for 1 : 467
Time taken for 10 : 467
Time taken for 100 : 466
Time taken for 1000 : 466
Time taken for 10000 : 466
Time taken for 100000 : 467
Time taken for 1000000 : 466
Time taken for 10000000 : 466
Time taken for 100000000 : 466
Time taken for 1000000000 : 466

如您所见,在第一个循环之后,无论输入是什么(模块噪声 - 基本上它总是 ~460ns 或 ~933ns,不可预测),这意味着 JIT 已经优化了循环输出。

如果您实际返回j并将初始值更改为i1而不是0,您将看到您期望的结果。i更改to的初始值1是因为否则 JIT 可以发现您最终将始终返回 0。

于 2013-07-20T11:02:30.990 回答
2

您实际上是在对 java 的 JIT 进行基准测试。如果我稍微修改一下你的代码:

class Test
{
    public static void main(String abc[])
    {
        for( int N=1; N <= 1_000_000_000; N=N*10)
        {
            long t1 = System.nanoTime();

            start(N);

            long t2 = System.nanoTime() - t1;
            System.out.println("Time taken for " + N + " : " + t2);
        }

        for( int N=1; N <= 1_000_000_000; N=N*10)
        {
            long t1 = System.nanoTime();

            start(N);

            long t2 = System.nanoTime() - t1;
            System.out.println("Time taken for " + N + " : " + t2);
        }
    }

    public static void start( int N )
    {
        int j=1;
        for(int i=0; i<=N; i++)
            j=j*i;
    }
}

我明白了:

Time taken for 1 : 1811
Time taken for 10 : 604
Time taken for 100 : 1510
Time taken for 1000 : 10565
Time taken for 10000 : 104439
Time taken for 100000 : 829173
Time taken for 1000000 : 604
Time taken for 10000000 : 302
Time taken for 100000000 : 0
Time taken for 1000000000 : 0
Time taken for 1 : 0
Time taken for 10 : 302
Time taken for 100 : 0
Time taken for 1000 : 302
Time taken for 10000 : 301
Time taken for 100000 : 302
Time taken for 1000000 : 0
Time taken for 10000000 : 0
Time taken for 100000000 : 0
Time taken for 1000000000 : 302

永远不要对“冷”系统进行基准测试。总是重复每次测量几次并丢弃第一次测量,因为优化尚未开始

于 2013-07-20T11:04:36.773 回答
1

原因是 1) 你不返回值,2) 计算的结果总是 0。最终 JIT 将简单地编译循环。

如果您将循环更改为:

public static int start(int N) {
    int j = 1;
    for (int i = 1; i <= N; i++)
        j = j * i;
    return j;
}

请注意,我已将循环 init 更改为int i = 1并添加了return j. 如果我只做其中一个,循环将(最终)仍然被编译掉。

这将产生以下系列(如果执行两次):

Time taken for 1 : 2934
Time taken for 10 : 1466
Time taken for 100 : 3422
Time taken for 1000 : 20534
Time taken for 10000 : 191644
Time taken for 100000 : 1898845
Time taken for 1000000 : 1210489
Time taken for 10000000 : 11884401
Time taken for 100000000 : 115257525
Time taken for 1000000000 : 1061254223
Time taken for 1 : 978
Time taken for 10 : 978
Time taken for 100 : 978
Time taken for 1000 : 2444
Time taken for 10000 : 11244
Time taken for 100000 : 103644
Time taken for 1000000 : 1030089
Time taken for 10000000 : 10448535
Time taken for 100000000 : 107299391
Time taken for 1000000000 : 1072580803
于 2013-07-20T11:09:00.407 回答