You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by rs...@apache.org on 2021/12/20 17:54:37 UTC

[avro] 04/04: AVRO-3243: Workaround JDK-8161372 - perf issue in ConcurrentHashMap#computeIfAbsent() (#1392)

This is an automated email from the ASF dual-hosted git repository.

rskraba pushed a commit to branch branch-1.11
in repository https://gitbox.apache.org/repos/asf/avro.git

commit ad63664df02bf5bfb63a6a4b3fefccede0e19352
Author: Martin Grigorov <ma...@users.noreply.github.com>
AuthorDate: Mon Dec 20 19:47:14 2021 +0200

    AVRO-3243: Workaround JDK-8161372 - perf issue in ConcurrentHashMap#computeIfAbsent() (#1392)
---
 .../java/org/apache/avro/reflect/ReflectData.java  |  6 ++-
 .../org/apache/avro/specific/SpecificData.java     |  6 ++-
 .../main/java/org/apache/avro/util/MapUtil.java    | 45 ++++++++++++++++++++++
 .../org/apache/avro/grpc/ServiceDescriptor.java    |  5 ++-
 4 files changed, 56 insertions(+), 6 deletions(-)

diff --git a/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java b/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java
index 4ead6b8..1af2581 100644
--- a/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java
+++ b/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java
@@ -36,6 +36,7 @@ import org.apache.avro.io.DatumWriter;
 import org.apache.avro.specific.FixedSize;
 import org.apache.avro.specific.SpecificData;
 import org.apache.avro.util.ClassUtils;
+import org.apache.avro.util.MapUtil;
 
 import java.io.IOException;
 import java.lang.annotation.Annotation;
@@ -63,6 +64,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 /** Utilities to use existing Java classes and interfaces via reflection. */
 public class ReflectData extends SpecificData {
@@ -826,11 +828,11 @@ public class ReflectData extends SpecificData {
     }
   }
 
-  private static final Map<Class<?>, Field[]> FIELDS_CACHE = new ConcurrentHashMap<>();
+  private static final ConcurrentMap<Class<?>, Field[]> FIELDS_CACHE = new ConcurrentHashMap<>();
 
   // Return of this class and its superclasses to serialize.
   private static Field[] getCachedFields(Class<?> recordClass) {
-    return FIELDS_CACHE.computeIfAbsent(recordClass, rc -> getFields(rc, true));
+    return MapUtil.computeIfAbsent(FIELDS_CACHE, recordClass, rc -> getFields(rc, true));
   }
 
   private static Field[] getFields(Class<?> recordClass, boolean excludeJava) {
diff --git a/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificData.java b/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificData.java
index 5b53939..8efd904 100644
--- a/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificData.java
+++ b/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificData.java
@@ -30,6 +30,7 @@ import org.apache.avro.io.DatumWriter;
 import org.apache.avro.io.DecoderFactory;
 import org.apache.avro.io.EncoderFactory;
 import org.apache.avro.util.ClassUtils;
+import org.apache.avro.util.MapUtil;
 import org.apache.avro.util.internal.ClassValueCache;
 
 import java.io.ObjectInput;
@@ -48,6 +49,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
 
 /** Utilities for generated Java classes and interfaces. */
@@ -228,7 +230,7 @@ public class SpecificData extends GenericData {
     return (datum instanceof Enum) ? getSchema(datum.getClass()) : super.getEnumSchema(datum);
   }
 
-  private Map<String, Class> classCache = new ConcurrentHashMap<>();
+  private final ConcurrentMap<String, Class> classCache = new ConcurrentHashMap<>();
 
   private static final Class NO_CLASS = new Object() {
   }.getClass();
@@ -251,7 +253,7 @@ public class SpecificData extends GenericData {
       String name = schema.getFullName();
       if (name == null)
         return null;
-      Class<?> c = classCache.computeIfAbsent(name, n -> {
+      Class<?> c = MapUtil.computeIfAbsent(classCache, name, n -> {
         try {
           return ClassUtils.forName(getClassLoader(), getClassName(schema));
         } catch (ClassNotFoundException e) {
diff --git a/lang/java/avro/src/main/java/org/apache/avro/util/MapUtil.java b/lang/java/avro/src/main/java/org/apache/avro/util/MapUtil.java
new file mode 100644
index 0000000..1bdbfaf
--- /dev/null
+++ b/lang/java/avro/src/main/java/org/apache/avro/util/MapUtil.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.util;
+
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+
+public class MapUtil {
+
+  private MapUtil() {
+    super();
+  }
+
+  /**
+   * A temporary workaround for Java 8 specific performance issue JDK-8161372
+   * .<br>
+   * This class should be removed once we drop Java 8 support.
+   *
+   * @see <a href=
+   *      "https://bugs.openjdk.java.net/browse/JDK-8161372">JDK-8161372</a>
+   */
+  public static <K, V> V computeIfAbsent(ConcurrentMap<K, V> map, K key, Function<K, V> mappingFunction) {
+    V value = map.get(key);
+    if (value != null) {
+      return value;
+    }
+    return map.computeIfAbsent(key, mappingFunction::apply);
+  }
+
+}
diff --git a/lang/java/grpc/src/main/java/org/apache/avro/grpc/ServiceDescriptor.java b/lang/java/grpc/src/main/java/org/apache/avro/grpc/ServiceDescriptor.java
index 0984473..bfb8ec2 100644
--- a/lang/java/grpc/src/main/java/org/apache/avro/grpc/ServiceDescriptor.java
+++ b/lang/java/grpc/src/main/java/org/apache/avro/grpc/ServiceDescriptor.java
@@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import io.grpc.MethodDescriptor;
+import org.apache.avro.util.MapUtil;
 
 import static io.grpc.MethodDescriptor.generateFullMethodName;
 
@@ -49,7 +50,7 @@ class ServiceDescriptor {
    */
   public static ServiceDescriptor create(Class iface) {
     String serviceName = AvroGrpcUtils.getServiceName(iface);
-    return SERVICE_DESCRIPTORS.computeIfAbsent(serviceName, key -> new ServiceDescriptor(iface, serviceName));
+    return MapUtil.computeIfAbsent(SERVICE_DESCRIPTORS, serviceName, key -> new ServiceDescriptor(iface, serviceName));
   }
 
   /**
@@ -67,7 +68,7 @@ class ServiceDescriptor {
    * @return a {@link MethodDescriptor}
    */
   public MethodDescriptor<Object[], Object> getMethod(String methodName, MethodDescriptor.MethodType methodType) {
-    return methods.computeIfAbsent(methodName,
+    return MapUtil.computeIfAbsent(methods, methodName,
         key -> MethodDescriptor.<Object[], Object>newBuilder()
             .setFullMethodName(generateFullMethodName(serviceName, methodName)).setType(methodType)
             .setRequestMarshaller(new AvroRequestMarshaller(protocol.getMessages().get(methodName)))