Synchronizing on methods than on code blocks is faster

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Synchronizing on methods than on code blocks is faster

Ionut

Hello All,

    I am trying to understand why "Synchronizing on methods rather than on code blocks is slightly faster" (source here https://weblogs.java.net/blog/johnsmart/archive/2008/03/using_hudson_en.html ). 

public synchronized void testSynchMethod() {
   x++;
}

VS

public void testSynchBlock() {
    synchronized (this) {
        x++;
    }
}

For this I did a trivial  JMH test and proved it. 

Could somebody please explain to me why is this happening? May be the explanation stays in the assembly code generated ...
Regards
Ionut

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Synchronizing on methods than on code blocks is faster

Aleksey Shipilev-2
On 09/28/2015 12:26 PM, Ionut wrote:
> I am trying to understand why "*Synchronizing on methods rather than
> on code blocks is slightly faster*" (source here
> https://weblogs.java.net/blog/johnsmart/archive/2008/03/using_hudson_en.html
> ).

This link seems irrelevant, no source code there.

> /public synchronized void testSynchMethod() {/ /   x++;/ /}/
>
> VS
>
> /public void testSynchBlock() {/ /    synchronized (this) {/ /
> x++;/ /    }/ /}/
>
> For this I did a trivial  JMH test and proved it.
>
> Could somebody please explain to me why is this happening? May be
> the explanation stays in the assembly code generated ...
Well? Where is that "trivial JMH test", and where is the proof with the
generated machine code?

Thanks,
-Aleksey


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Synchronizing on methods than on code blocks is faster

Ionut
Hi,

   Please find the sample test attached. I have launched the test with -XX:BiasedLockingStartupDelay=0

The output I got is:
testSynchMethod() - 358 ops / µsec
testSynchBlock() - 345 ops / µsec

This is a slightly better throughput in case of testSynchMethod(). My assumption is that probably native code generated makes this difference, but I am not sure, that's why I am posting the question here.

Please correct me if I my test is wrong or I a miss something.

Regards
Ionut


On Monday, September 28, 2015 1:11 PM, Aleksey Shipilev <[hidden email]> wrote:


On 09/28/2015 12:26 PM, Ionut wrote:
> I am trying to understand why "*Synchronizing on methods rather than
> on code blocks is slightly faster*" (source here
> https://weblogs.java.net/blog/johnsmart/archive/2008/03/using_hudson_en.html
> ).

This link seems irrelevant, no source code there.


> /public synchronized void testSynchMethod() {/ /  x++;/ /}/
>
> VS
>
> /public void testSynchBlock() {/ /    synchronized (this) {/ /
> x++;/ /    }/ /}/
>
> For this I did a trivial  JMH test and proved it.
>
> Could somebody please explain to me why is this happening? May be
> the explanation stays in the assembly code generated ...

Well? Where is that "trivial JMH test", and where is the proof with the
generated machine code?

Thanks,
-Aleksey




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

SynchronizedMethodVsBlockMain.java (2K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Synchronizing on methods than on code blocks is faster

Aleksey Shipilev-2
On 09/28/2015 04:40 PM, Ionut wrote:
> Please correct me if I my test is wrong or I a miss something.

I don't understand a few things:

 a) What are the errors for your measurements? You can't say "slightly
better" unless you can statistically infer it (at very least by
assessing the margins of error).

 b) The code fragments in SynchMethod and SynchBlock are not, strictly
speaking, semantically equivalent: SynchBlock does the unsynchronized
counter.x read. This already partially invalidates the evidence without
the analysis whether this semantic difference is nil in practice.

 c) Why this benchmark is asymmetric with @Group. Each group in your
benchmark has a single method, which means the benchmark is actually
symmetric, and the entire @Group business can be dropped.

 d) Why forks(0)? This is unreliable when estimating the run-to-run
variance, and some other tricks to work.

 e) Why explicit CounterStructure? You may as well put the field in the
enclosing class, since it's already @State.


Nevertheless, this benchmark provides the same performance in both test
on my i7-4790K, Linux x86_64, JDK 8u40:

  @BenchmarkMode(Mode.Throughput)
  @OutputTimeUnit(TimeUnit.MICROSECONDS)
  @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
  @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
  @Fork(value = 3, jvmArgsAppend = "-XX:BiasedLockingStartupDelay=0")
  @State(Scope.Benchmark)
  public class SMvSB {
      int x;

      @Benchmark
      @CompilerControl(CompilerControl.Mode.DONT_INLINE)
      public synchronized int syncMethod() {
          x++;
          return x;
      }

      @Benchmark
      @CompilerControl(CompilerControl.Mode.DONT_INLINE)
      public int syncBlock() {
          synchronized (this) {
              x++;
          }
          return x;
      }
  }

  Benchmark          Mode  Cnt    Score   Error   Units
  SMvSB.syncBlock   thrpt   15  244.156 ± 1.048  ops/us
  SMvSB.syncMethod  thrpt   15  244.913 ± 0.775  ops/us

...and this is because both tests use biased locks, and both syncBlock
and syncMethod reused the "x++" value without reading it the second time
-- you can clearly see that with "-prof perfasm". (Or, in other
interpretation, one can think as "return x" soaked into synchronized block).

The same thing happens with -XX:-UseBiasedLocking, although with a
significant throughput hit:

  Benchmark          Mode  Cnt   Score   Error   Units
  SMvSB.syncBlock   thrpt   15  57.474 ± 0.494  ops/us
  SMvSB.syncMethod  thrpt   15  57.886 ± 0.107  ops/us

Explaining the throughput hit (not speculating/handwaving about it, but
actually explaining with the profiling evidence: -prof perfasm and -prof
perfnorm are your friends here) is left as an exercise for a reader.
Don't shrug it off, it is actually a good and simple exercise in
benchmarking.

Thanks,
-Aleksey



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Synchronizing on methods than on code blocks is faster

Ionut
Hello Alexey,

    Thanks, it is clear now for me, I did some mistakes in writing the JMH test.

I have re-launched the corrected test and got the same throughput.

Appreciate your input !

Regards,
Ionut



On Monday, September 28, 2015 5:19 PM, Aleksey Shipilev <[hidden email]> wrote:


On 09/28/2015 04:40 PM, Ionut wrote:
> Please correct me if I my test is wrong or I a miss something.

I don't understand a few things:

a) What are the errors for your measurements? You can't say "slightly
better" unless you can statistically infer it (at very least by
assessing the margins of error).

b) The code fragments in SynchMethod and SynchBlock are not, strictly
speaking, semantically equivalent: SynchBlock does the unsynchronized
counter.x read. This already partially invalidates the evidence without
the analysis whether this semantic difference is nil in practice.

c) Why this benchmark is asymmetric with @Group. Each group in your
benchmark has a single method, which means the benchmark is actually
symmetric, and the entire @Group business can be dropped.

d) Why forks(0)? This is unreliable when estimating the run-to-run
variance, and some other tricks to work.

e) Why explicit CounterStructure? You may as well put the field in the
enclosing class, since it's already @State.


Nevertheless, this benchmark provides the same performance in both test
on my i7-4790K, Linux x86_64, JDK 8u40:

  @BenchmarkMode(Mode.Throughput)
  @OutputTimeUnit(TimeUnit.MICROSECONDS)
  @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
  @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
  @Fork(value = 3, jvmArgsAppend = "-XX:BiasedLockingStartupDelay=0")
  @State(Scope.Benchmark)
  public class SMvSB {
      int x;

      @Benchmark
      @CompilerControl(CompilerControl.Mode.DONT_INLINE)
      public synchronized int syncMethod() {
          x++;
          return x;
      }

      @Benchmark
      @CompilerControl(CompilerControl.Mode.DONT_INLINE)
      public int syncBlock() {
          synchronized (this) {
              x++;
          }
          return x;
      }
  }

  Benchmark          Mode  Cnt    Score  Error  Units
  SMvSB.syncBlock  thrpt  15  244.156 ± 1.048  ops/us
  SMvSB.syncMethod  thrpt  15  244.913 ± 0.775  ops/us

...and this is because both tests use biased locks, and both syncBlock
and syncMethod reused the "x++" value without reading it the second time
-- you can clearly see that with "-prof perfasm". (Or, in other
interpretation, one can think as "return x" soaked into synchronized block).

The same thing happens with -XX:-UseBiasedLocking, although with a
significant throughput hit:

  Benchmark          Mode  Cnt  Score  Error  Units
  SMvSB.syncBlock  thrpt  15  57.474 ± 0.494  ops/us
  SMvSB.syncMethod  thrpt  15  57.886 ± 0.107  ops/us

Explaining the throughput hit (not speculating/handwaving about it, but
actually explaining with the profiling evidence: -prof perfasm and -prof
perfnorm are your friends here) is left as an exercise for a reader.
Don't shrug it off, it is actually a good and simple exercise in
benchmarking.


Thanks,
-Aleksey




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest