[concurrency-interest] Synchronizing on methods than on code blocks is faster

Ionut ionutb83 at yahoo.com
Thu Oct 1 03:09:36 EDT 2015


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 <aleksey.shipilev at oracle.com> 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



  
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20151001/56d00043/attachment.html>


More information about the Concurrency-interest mailing list