What is the synchronization cost of calling a synchronized method from a synchronized method?

后端 未结 6 1725
梦谈多话
梦谈多话 2021-01-01 13:56

Is there any difference in performance between this

synchronized void x() {
    y();
}

synchronized void y() {
}

and this

         


        
6条回答
  •  独厮守ぢ
    2021-01-01 14:23

    Test can be found below ( You have to guess what some methods do but nothing complicated ) :

    It tests them with 100 threads each and starts counting the averages after 70% of them has completed ( as warmup ).

    It prints it out once at the end.

    public static final class Test {
            final int                      iterations     =     100;
            final int                      jiterations    = 1000000;
            final int                      count          = (int) (0.7 * iterations);
            final AtomicInteger            finishedSingle = new AtomicInteger(iterations);
            final AtomicInteger            finishedZynced = new AtomicInteger(iterations);
            final MovingAverage.Cumulative singleCum      = new MovingAverage.Cumulative();
            final MovingAverage.Cumulative zyncedCum      = new MovingAverage.Cumulative();
            final MovingAverage            singleConv     = new MovingAverage.Converging(0.5);
            final MovingAverage            zyncedConv     = new MovingAverage.Converging(0.5);
    
            // -----------------------------------------------------------
            // -----------------------------------------------------------
            public static void main(String[] args) {
                    final Test test = new Test();
    
                    for (int i = 0; i < test.iterations; i++) {
                            test.benchmark(i);
                    }
    
                    Threads.sleep(1000000);
            }
            // -----------------------------------------------------------
            // -----------------------------------------------------------
    
            void benchmark(int i) {
    
                    Threads.async(()->{
                            long start = System.nanoTime();
    
                            for (int j = 0; j < jiterations; j++) {
                                    a();
                            }
    
                            long elapsed = System.nanoTime() - start;
                            int v = this.finishedSingle.decrementAndGet();
                            if ( v <= count ) {
                                    singleCum.add (elapsed);
                                    singleConv.add(elapsed);
                            }
    
                            if ( v == 0 ) {
                                    System.out.println(elapsed);
                                    System.out.println("Single Cum:\t\t" + singleCum.val());
                                    System.out.println("Single Conv:\t" + singleConv.val());
                                    System.out.println();
    
                            }
                    });
    
                    Threads.async(()->{
    
                            long start = System.nanoTime();
                            for (int j = 0; j < jiterations; j++) {
                                    az();
                            }
    
                            long elapsed = System.nanoTime() - start;
    
                            int v = this.finishedZynced.decrementAndGet();
                            if ( v <= count ) {
                                    zyncedCum.add(elapsed);
                                    zyncedConv.add(elapsed);
                            }
    
                            if ( v == 0 ) {
                                    // Just to avoid the output not overlapping with the one above 
                                    Threads.sleep(500);
                                    System.out.println();
                                    System.out.println("Zynced Cum: \t"  + zyncedCum.val());
                                    System.out.println("Zynced Conv:\t" + zyncedConv.val());
                                    System.out.println();
                            }
                    });
    
            }                       
    
            synchronized void a() { b();  }
                         void b() { c();  }
                         void c() { d();  }
                         void d() { e();  }
                         void e() { f();  }
                         void f() { g();  }
                         void g() { h();  }
                         void h() { i();  }
                         void i() { }
    
            synchronized void az() { bz(); }
            synchronized void bz() { cz(); }
            synchronized void cz() { dz(); }
            synchronized void dz() { ez(); }
            synchronized void ez() { fz(); }
            synchronized void fz() { gz(); }
            synchronized void gz() { hz(); }
            synchronized void hz() { iz(); }
            synchronized void iz() {}
    }
    

    MovingAverage.Cumulative add is basically ( performed atomically ): average = (average * (n) + number) / (++n);

    MovingAverage.Converging you can look up but uses another formula.

    The results after a 50 second warmup:

    With: jiterations -> 1000000

    Zynced Cum:     3.2017985649516254E11
    Zynced Conv:    8.11945143126507E10
    
    Single Cum:     4.747368153507841E11
    Single Conv:    8.277793176290959E10
    

    That's nano seconds averages. That's really nothing and even shows that the zynced one takes less time.

    With: jiterations -> original * 10 (takes much longer time)

    Zynced Cum:     7.462005651190714E11
    Zynced Conv:    9.03751742946726E11
    
    Single Cum:     9.088230941676143E11
    Single Conv:    9.09877020004914E11
    

    As you can see the results show it's really not a big difference. The zynced one actually has lower average time for the last 30% completions.

    With one thread each (iterations = 1) and jiterations = original * 100;

    Zynced Cum:     6.9167088486E10
    Zynced Conv:    6.9167088486E10
    
    Single Cum:     6.9814404337E10
    Single Conv:    6.9814404337E10
    

    In a same thread environment ( removing Threads.async calls )

    With: jiterations -> original * 10

    Single Cum:     2.940499529542545E8
    Single Conv:    5.0342450600964054E7
    
    
    Zynced Cum:     1.1930525617915475E9
    Zynced Conv:    6.672312498662484E8
    

    The zynced one here seems to be slower. On an order of ~10. The reason for this could be due to the zynced one running after each time, who knows. No energy to try the reverse.

    Last test run with:

    public static final class Test {
            final int                      iterations     =     100;
            final int                      jiterations    = 10000000;
            final int                      count          = (int) (0.7 * iterations);
            final AtomicInteger            finishedSingle = new AtomicInteger(iterations);
            final AtomicInteger            finishedZynced = new AtomicInteger(iterations);
            final MovingAverage.Cumulative singleCum      = new MovingAverage.Cumulative();
            final MovingAverage.Cumulative zyncedCum      = new MovingAverage.Cumulative();
            final MovingAverage            singleConv     = new MovingAverage.Converging(0.5);
            final MovingAverage            zyncedConv     = new MovingAverage.Converging(0.5);
    
            // -----------------------------------------------------------
            // -----------------------------------------------------------
            public static void main(String[] args) {
                    final Test test = new Test();
    
                    for (int i = 0; i < test.iterations; i++) {
                            test.benchmark(i);
                    }
    
                    Threads.sleep(1000000);
            }
            // -----------------------------------------------------------
            // -----------------------------------------------------------
    
            void benchmark(int i) {
    
                            long start = System.nanoTime();
    
                            for (int j = 0; j < jiterations; j++) {
                                    a();
                            }
    
                            long elapsed = System.nanoTime() - start;
                            int s = this.finishedSingle.decrementAndGet();
                            if ( s <= count ) {
                                    singleCum.add (elapsed);
                                    singleConv.add(elapsed);
                            }
    
                            if ( s == 0 ) {
                                    System.out.println(elapsed);
                                    System.out.println("Single Cum:\t\t" + singleCum.val());
                                    System.out.println("Single Conv:\t" + singleConv.val());
                                    System.out.println();
    
                            }
    
    
                            long zstart = System.nanoTime();
                            for (int j = 0; j < jiterations; j++) {
                                    az();
                            }
    
                            long elapzed = System.nanoTime() - zstart;
    
                            int z = this.finishedZynced.decrementAndGet();
                            if ( z <= count ) {
                                    zyncedCum.add(elapzed);
                                    zyncedConv.add(elapzed);
                            }
    
                            if ( z == 0 ) {
                                    // Just to avoid the output not overlapping with the one above 
                                    Threads.sleep(500);
                                    System.out.println();
                                    System.out.println("Zynced Cum: \t"  + zyncedCum.val());
                                    System.out.println("Zynced Conv:\t" + zyncedConv.val());
                                    System.out.println();
                            }
    
            }                       
    
            synchronized void a() { b();  }
                         void b() { c();  }
                         void c() { d();  }
                         void d() { e();  }
                         void e() { f();  }
                         void f() { g();  }
                         void g() { h();  }
                         void h() { i();  }
                         void i() { }
    
            synchronized void az() { bz(); }
            synchronized void bz() { cz(); }
            synchronized void cz() { dz(); }
            synchronized void dz() { ez(); }
            synchronized void ez() { fz(); }
            synchronized void fz() { gz(); }
            synchronized void gz() { hz(); }
            synchronized void hz() { iz(); }
            synchronized void iz() {}
    }
    

    Conclusion, there really is no difference.

提交回复
热议问题