简体   繁体   English

Java乘法奇怪的行为

[英]Java multiplication strange behaviour

Have a look at the code below: 看看下面的代码:

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;
    }
}

The output produced by the above question is: 上述问题产生的结果是:

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

Questions: 问题:

1.) Why is time taken for N=1 unusually greater than the N=10 ? 1.)为什么N = 1的时间异常大于N = 10? (sometimes it even exceeds N=100) (有时甚至超过N = 100)

2.) Why is time taken for N=10M and onwards unusually lower ? 2.)为什么N = 10M及以后的时间异常低?

The pattern indicated in the above questions is profound and remains even after many iterations. 上述问题中指出的模式是深刻的,甚至在经过多次迭代后仍然存在。 Is there any connection to memoization here ? 这里有记忆的连接吗?

EDIT: 编辑:

Thank you for your answers. 谢谢您的回答。 I thought of replacing the method call with the actual loop. 我想用实际循环替换方法调用。 But now, there is no JIT Optimization. 但现在,没有JIT优化。 Why not ? 为什么不 ? Is putting the statements in a method facilitating in the optimization process ? 是否将语句放在促进优化过程的方法中? The modified code is below: 修改后的代码如下:

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);
            }
        }
    }
}

EDIT 2: The output of above modified code: 编辑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

1.) Why is time taken for N=1 unusually greater than the N=10 1.)为什么N = 1的时间异常大于N = 10

Because it's the first time the VM has seen that code - it may decide to just interpret it, or it will take a little bit of time JITting it to native code, but probably without optimization. 因为这是VM首次看到该代码 - 它可能决定只是解释它,或者需要花一点时间将它JIT化为本机代码,但可能没有优化。 This is one of the "gotchas" of benchmarking Java. 这是Java基准测试的“陷阱”之一。

2.) Why is time taken for N=10M and onwards unusually lower ? 2.)为什么N = 10M及以后的时间异常低?

At that point, the JIT has worked harder to optimize the code - reducing it to almost nothing. 那时,JIT更加努力地优化代码 - 将其减少到几乎为零。

In particular, if you run this code multiple times (just in a loop), you'll see the effect of the JIT compiler optimizing: 特别是,如果多次运行此代码(仅在循环中),您将看到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

As you can see, after the first the loop takes the same amount of time whatever the input (module noise - basically it's always either ~460ns or ~933ns, unpredictably) which means the JIT has optimized the loop out. 正如您所看到的,在第一个循环之后,无论输入是什么时间(模块噪声 - 基本上它总是~460ns或~933ns,不可预测),这意味着JIT优化了循环。

If you actually returned j , and changed the initial value of i to 1 instead of 0 , you'll see the kind of results you expect. 如果您实际返回了j并将 i的初始值更改为1而不是0 ,您将看到所期望的结果类型。 The change of the initial value of i to 1 is because otherwise the JIT can spot that you'll always end up returning 0. i的初始值更改为1是因为否则JIT可以发现您总是最终返回0。

youre actually benchmarking java's JIT. 你实际上是对Java的JIT进行基准测试。 if i modify yout code a bit: 如果我修改你的代码:

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;
    }
}

i get this: 我明白了:

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

never benchmark a "cold" system. 从不对“冷”系统进行基准测试。 always repeat every measurement several times and discard the 1st few ones because the optimizations have not yet kicked in 总是重复每次测量几次并丢弃前几次测量,因为优化还没有开始

The reason is that 1) you don't return the value, and 2) the result of the calculation is always 0. Eventually the JIT will simply compile the loop away. 原因是1)你没有返回值,2)计算结果总是0.最后JIT将简单地编译循环。

You get your expected behaviour if you change your loop to: 如果将循环更改为以下内容,则会得到预期的行为:

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

Note that I have both changed the loop init to int i = 1 and added return j . 请注意,我已将循环init更改为int i = 1并添加了return j If I only do one of those, the loop will (eventually) still be compiled away. 如果我只做其中一个,那么循环(最终)仍会被编译掉。

This will produce the following series (if executed twice): 这将产生以下系列(如果执行两次):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM