[lworld] VarHandle performance might need an investigation

XMLWordPrintable

      VarHandle APIs have been designed before Valhalla and the introduction of flat values.
      The VarHandle implementation has been modified to support flat values when possible. These modifications increased the complexity of the VarHandle code. One challenge is that the VarHandle APIs are based on Java types, but layout characteristics and other additional properties (like null-freenss) are not part of the Java type. This prevents the implementation of a VarHandle to cache this information and instead, forces it to fetch them whenever an access has to be performed.

      For instance, here's the implementation of VarHandle.compareAndExchange for arrays:

          @ForceInline
          static Object storeCheck(ArrayVarHandle handle, Object[] oarray, Object value) {
              if (value == null && ValueClass.isNullRestrictedArray(oarray)) {
                  throw new NullPointerException("null not allowed for null-restricted array " + oarray.getClass().toGenericString());
              }
              if (handle.arrayType == oarray.getClass()) {
                  // Fast path: static array type same as argument array type
                  return handle.componentType.cast(value);
              } else {
                  // Slow path: check value against argument array component type
                  return reflectiveTypeCheck(oarray, value);
              }
          }

          @ForceInline
          static Object compareAndExchange(VarHandle ob, Object oarray, int index, Object expected, Object value) {
              ArrayVarHandle handle = (ArrayVarHandle) ob;
              Object[] array = (Object[]) handle.arrayType.cast(oarray);
              Class<?> arrayType = oarray.getClass();
              if (ValueClass.isFlatArray(oarray)) {
                  // delegate to flat access primitives
                  VarHandles.checkAtomicFlatArray(array);
                  int aoffset = (int) UNSAFE.arrayInstanceBaseOffset(array);
                  int ascale = UNSAFE.arrayInstanceIndexScale(array);
                  int ashift = 31 - Integer.numberOfLeadingZeros(ascale);
                  int layout = UNSAFE.arrayLayout(array);
                  return UNSAFE.compareAndExchangeFlatValue(array,
                          (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << ashift) + aoffset, layout, arrayType.componentType(),
                          arrayType.componentType().cast(expected),
                          storeCheck(handle, array, value));
              }
              return UNSAFE.compareAndExchangeReference(array,
                      (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << REFERENCE_SHIFT) + REFERENCE_BASE, handle.componentType,
                      handle.componentType.cast(expected),
                      storeCheck(handle, array, value));
          }

      This code has multiple calls ValueClass.* methods and Unsafe.* methods, notably to fetch the base, the index scale and the layout of the array in case of a flat array. These methods have intrinsics for their access to VM meta-data, but they are still representing an overhead, and some of them are not even annotated with @ForceInline. So, the impact on performance is not clear, especially
      considering that these calls are performed each time the compareAndExchange() method is called, whereas in the original version, these values are cached in the VarHandle instance.


      By comparison, here's the code from mainline for the same method:

              @ForceInline
              static Object runtimeTypeCheck(Array handle, Object[] oarray, Object value) {
                  if (handle.arrayType == oarray.getClass()) {
                      // Fast path: static array type same as argument array type
                      return handle.componentType.cast(value);
                  } else {
                      // Slow path: check value against argument array component type
                      return reflectiveTypeCheck(oarray, value);
                  }
              }

      @ForceInline
              static Object compareAndExchange(VarHandle ob, Object oarray, int index, Object expected, Object value) {
                  Array handle = (Array)ob;
                  Object[] array = (Object[]) handle.arrayType.cast(oarray);
                  return UNSAFE.compareAndExchangeReference(array,
                          (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
                          handle.componentType.cast(expected),
                          runtimeTypeCheck(handle, array, value));
              }


      If VarHandle.compareAndExchange() is called on performance critical code, it might worth measuring the performance impact of these modifications.

      Also, the ValueClass.isFlatArray(oarray) test is performed in both preview and non-preview mode.
      It should be fast (just checking a bit in the array's header) but still another overhead added to mainline in normal mode.


            Assignee:
            Chen Liang
            Reporter:
            Frederic Parain
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: