I tried to create a JMH to show the difference between various injection
techniques. While I am familiar with JMH and how to write them, I make no
claims that this is perfect. Also, I did not do any kind of analysis, such
as looking into the hot methods assembly.

I made eight different benchmarks, covering the following:

1. Set field directly (control).
2. Set field via setter method (alternate control).
3. Set field via reflection using Field object, not synchronized.
4. Set field via reflection using Method object for setter method, not
synchronized.
5. Set field via reflection using Field object, synchronized.
6. Set field via reflection using Method object for setter method,
synchronized.
7. Set field via MethodHandle using a handle to the field setter.
8. Set field via MethodHandle using a handle to the setter method.

You can see the full results & code at [1]. I only kept a few of the best
(lowest error value) runs. One of the best runs I have copied below, but
they are best viewed in monospace font, like at [1].

These numbers are specific to my machine, so the important inference that
can be gained from these data is the relative, not absolute values.

Benchmark                                                      (str)  Mode
 Cnt    Score    Error  Units
SynchronizedBenchmark.viaField                                   str  avgt
  15    7.158 ±  0.181  ns/op
SynchronizedBenchmark.viaSetterMethod                            str  avgt
  15    7.802 ±  0.193  ns/op
SynchronizedBenchmark.viaMethodHandle_setterMethod               str  avgt
  15   17.766 ±  0.551  ns/op
SynchronizedBenchmark.viaMethodHandle_field                      str  avgt
  15   17.939 ±  0.290  ns/op
SynchronizedBenchmark.viaReflection_field                        str  avgt
  15  157.453 ±  2.678  ns/op
SynchronizedBenchmark.viaReflection_setterMethod                 str  avgt
  15  157.346 ±  2.049  ns/op
SynchronizedBenchmark.viaReflection_setterMethod_synchronized    str  avgt
  15  542.966 ± 19.825  ns/op
SynchronizedBenchmark.viaReflection_field_synchronized           str  avgt
  15  771.381 ± 47.922  ns/op

I added in using MethodHandles in the tests, just to show the difference.
When Sling stops supporting Java 8, we can move to MethodHandle which has
significantly better performance characteristics. Unfortunately, we can't
make that move until we are on at least Java 9, so that we can use
MethodHandles.privateLookupIn(..) [2].

Regardless, you can see the significant difference between synchronized vs
non-synchronized: synchronizing is 3-8x slower than not, when under
contention with multiple threads.

[1]:
https://gist.github.com/paul-bjorkstrand/f3bb154665e7d2168b4656eb7b794496
[2]:
https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#privateLookupIn-java.lang.Class-java.lang.invoke.MethodHandles.Lookup-
-Paul


On Sat, Dec 4, 2021 at 9:48 AM Paul Bjorkstrand <paul.bjorkstr...@gmail.com>
wrote:

> In light of discussion on a recently merged PR [1], I raised the question
> 'do we need to "reset" the accessible flag for reflective injectables
> (fields, methods, constructors)?'
>
> The sole purpose of synchronized around the injection logic (including
> constructor instantiation) seems to surround properly resetting the
> accessible flag on the reflection element, and ensuring the element is
> accessible during the injection.
>
> It is likely worth the effort to determine if resetting that flag, and
> thus the accompanying synchronized, is really needed. As a reference, I
> searched Apache Felix for uses of setAccessible, as it also needs to be
> able to inject into non-public elements [2]. After browsing a handful of
> the non-test classes, I found none that do anything more than
> setAccessible(true). This leads me to believe that we are doing extra work
> in Sling Models that isn't really necessary.
>
> Removing synchronized could have a dramatic, positive impact on
> performance, especially for situations where there may be high contention
> (e.g. heavy model/submodel reuse, sharing a common super class, etc). If
> those synchronizations were removed, overall throughput will likely
> increase.
>
> Refs:
> [1]:
> https://github.com/apache/sling-org-apache-sling-models-impl/pull/11/files#r762409049
> [2]: https://github.com/apache/felix-dev/search?q=setAccessible
>
> -Paul
>

Reply via email to