Improved Bindings.getBinding(Class) caching for Datatype.class 10/2010/2
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Fri, 17 Aug 2018 08:23:20 +0000 (11:23 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Fri, 17 Aug 2018 08:24:01 +0000 (08:24 +0000)
Previously Datatype.class binding was never cached nor statically
constructed and made available as Bindings.DATATYPE which is only
sensible because Datatype class is a central building block of Databoard
and therefore its binding should also be readily available instead of
all clients having to request for it separately.

A weak hashmap cache was also added for signature strings in
BindingRequest because it was noticed that in conditions where code
makes a lot of requests for uncached binding, the cost of String
construction in BindingRequest.getSignature starts to dominate
cpu/memory costs. Two main problems in there were: constantly creating
new strings and using the very inefficient String.replaceAll instead of
String.replace(char, char).

gitlab #82

Change-Id: I50fbbc2bbe1e5d6b8b5864c3aed4c228022e66dc

bundles/org.simantics.databoard/src/org/simantics/databoard/Bindings.java
bundles/org.simantics.databoard/src/org/simantics/databoard/Databoard.java
bundles/org.simantics.databoard/src/org/simantics/databoard/binding/VariantBinding.java
bundles/org.simantics.databoard/src/org/simantics/databoard/binding/factory/BindingRepository.java
bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/BindingRequest.java
bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/request/PropertyInfo.java

index 266391fa98eb340bcdff6e47b956e5777ccb31a8..138ca5c058970908ba96fdbbb76557503c0a9468 100644 (file)
@@ -134,7 +134,8 @@ public class Bindings {
        public static final VariantBinding STR_VARIANT;         // java.lang.String ( as variant )
        
        public static final Binding        VOID;            // void ( as {} )
-       public static final Binding        BEAN;            // Bean ( as variant )                      
+       public static final Binding        BEAN;            // Bean ( as variant )
+       public static final Binding        DATATYPE;        // org.simantics.databoard.type.Datatype
 
        public static final ArrayBinding   BOOLEAN_ARRAY;       // boolean[]    
        public static final ArrayBinding   BYTE_ARRAY;          // byte[]
@@ -890,7 +891,9 @@ public class Bindings {
        OBJECT                           = databoard.OBJECT;
        
        databoard.initialize();
+       
+               DATATYPE = getBindingUnchecked(Datatype.class);
     }
-    
+
 }
 
index ef4b0732d356cfa332489f5e30e6635d5e83ba24..da488e0a81b087f423b0bfe0171102f305bf625f 100644 (file)
@@ -153,6 +153,7 @@ public class Databoard {
                        BindingRequest br = new BindingRequest( Datatype.class ); 
                        Binding datatypeBinding = getBinding( br );
                        typeClassFactory.getRepository().put(datatypeBinding.type(), br);
+                       bindingRepository.put(br, datatypeBinding);
                } catch (ClassNotFoundException e) {
                } catch (InstantiationException e) {
                } catch (IllegalAccessException e) {
index 30fe63ffae1ee7ae4d0472add679a5a1d2c91100..60e01409020d399d2f15c1ac2083960a8eed9e06 100644 (file)
@@ -174,11 +174,10 @@ public abstract class VariantBinding extends Binding {
 
     @Override
     public int deepHashValue(Object value, IdentityHashMap<Object, Object> hashedObjects) throws BindingException {
-               Datatype type = getContentType(value);          
+               Datatype type = getContentType(value);
                Binding binding = getContentBinding(value);
-               Binding dataTypeBinding = Bindings.getBindingUnchecked(Datatype.class);
        Object element = getContent(value, binding);
-       return dataTypeBinding.deepHashValue(type, hashedObjects) + binding.deepHashValue(element, hashedObjects);
+       return Bindings.DATATYPE.deepHashValue(type, hashedObjects) + binding.deepHashValue(element, hashedObjects);
     }
 
        @Override
@@ -188,8 +187,7 @@ public abstract class VariantBinding extends Binding {
                // Compare Type
                Datatype t1 = getContentType(o1);
                Datatype t2 = getContentType(o2);
-               Binding dataTypeBinding = Bindings.getBindingUnchecked(Datatype.class);
-               int dif = dataTypeBinding.compare(t1, t2);
+               int dif = Bindings.DATATYPE.compare(t1, t2);
                if (dif!=0) return dif;
                // Compare Value
                Binding bi1 = getContentBinding(o1);
index 4680eee6a961b9d147fc5c0b3cb4478ae49a5e0e..ba24353712c7f90f415b4b2652f7699dba5b27d0 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
+ * Copyright (c) 2007, 2018 Association for Decentralized Information Management in
  * Industry THTH ry.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,7 @@
  *
  * Contributors:
  *     VTT Technical Research Centre of Finland - initial API and implementation
+ *     Semantum Oy - gitlab #82
  *******************************************************************************/
 package org.simantics.databoard.binding.factory;
 
@@ -17,6 +18,7 @@ import java.util.Map.Entry;
 
 import org.simantics.databoard.binding.Binding;
 import org.simantics.databoard.binding.reflection.BindingRequest;
+import org.simantics.databoard.type.Datatype;
 
 public class BindingRepository {
        
@@ -114,9 +116,20 @@ public class BindingRepository {
                classMap.clear();
        }
 
+       /**
+        * {@link Datatype} class has annotations but it can be considered a "class
+        * request" as it is a fundamental building block of Databoard and it has a
+        * fixed structure. Therefore {@link #classMap} is allowed to contain a cached
+        * Datatype.class -> Binding mapping.
+        */
+       private static final String DATATYPE_CLASS_NAME = Datatype.class.getName();
+
        boolean isClassRequest( BindingRequest request )
        {
-               return request.className != null && (request.annotations==null || request.annotations.length==0);
+               return (request.className != null
+                               && ((request.annotations==null || request.annotations.length==0)
+                                               || DATATYPE_CLASS_NAME.equals(request.className))
+                               );
        }
 
 }
index 4ada936032036b149dd182ac6bb005e138234ffd..51feb39c3815861e4edde721cb4970ddf33898c0 100644 (file)
@@ -1,3 +1,15 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2018 Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *     Semantum Oy - gitlab #82
+ *******************************************************************************/
 package org.simantics.databoard.binding.reflection;
 
 import java.lang.annotation.Annotation;
@@ -6,13 +18,25 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
 import org.simantics.databoard.annotations.ArgumentImpl;
 import org.simantics.databoard.annotations.Arguments;
 import org.simantics.databoard.binding.Binding;
 import org.simantics.databoard.primitives.MutableInteger;
 
 public class BindingRequest {
-       
+
+    /**
+     * A weak cache for signature strings by Class.
+     * Prevents the system from constructing new strings
+     * from Classes for every non-trivial BindingRequest. 
+     */
+    private static final Map<Class<?>, String> signatureCache = Collections.<Class<?>, String>synchronizedMap(new WeakHashMap<>());
+
+    public static final Annotation[] NO_ANNOTATIONS = {};
+
        public static BindingRequest create( Field field )
        {
        Annotation[] annotations = ClassBindingFactory.getFieldAnnotations(field);
@@ -27,8 +51,6 @@ public class BindingRequest {
     /** Annotations */
     public final Annotation[] annotations;
     
-    public final Annotation[] NO_ANNOTATIONS = new Annotation[0];
-    
     public final String className; // eg. java.util.Map
     public final String signature; // eg. Ljava/util/Map;
     public final String descriptor; //eg. Ljava/util/Map<I;I>;
@@ -59,7 +81,18 @@ public class BindingRequest {
             hash = 7*hash + a.hashCode();
         }
     }
-    
+
+    /**
+     * Create BindingRequest
+     * 
+     * @param clazz
+     * @param annotations
+     */
+    public BindingRequest(Class<?> clazz)
+    {
+        this(clazz, NO_ANNOTATIONS);
+    }
+
     /**
      * Create BindingRequest
      * 
@@ -221,8 +254,17 @@ public class BindingRequest {
                if (clazz==float.class) return "F";
                if (clazz==long.class) return "J";
                if (clazz==double.class) return "D";
-               if (clazz.isArray()) return clazz.getName().replaceAll("\\.", "/");             
-               return "L"+clazz.getName().replaceAll("\\.", "/")+";";
+               String cached = signatureCache.get(clazz);
+               if (cached == null) {
+                       cached = clazz.isArray()
+                                       ? clazz.getName().replace('.', '/')
+                                       : "L"+clazz.getName().replace('.', '/')+";";
+                       signatureCache.put(clazz, cached);
+                       //System.out.println("BindingRequest.getSignature: cache miss for " + clazz + " = " + cached);
+               } else {
+                       //System.out.println("BindingRequest.getSignature: cache hit for " + clazz + " = " + cached);
+               }
+               return cached;
     }
     
     @SuppressWarnings("unchecked")
index aafcef3b2a65905e15c11111f326000650d5475e..f3753ce80bfc3949c84914c234c14f76520c29e4 100644 (file)
@@ -51,7 +51,7 @@ public class PropertyInfo {
                if(literalRange != null) {
                        Collection<Resource> dts = graph.getAssertedObjects(literalRange, L0.HasDataType);
                        if(dts.size() == 1) {
-                               Datatype dt = graph.getPossibleValue(dts.iterator().next(), Bindings.getBindingUnchecked(Datatype.class));
+                               Datatype dt = graph.getPossibleValue(dts.iterator().next(), Bindings.DATATYPE);
                                if(requiredDatatype == null) requiredDatatype = dt;
                        }
                }