Grails
  1. Grails
  2. GRAILS-8130

Optimize Grails AST "instance api" performance for plain getter (properties)

    Details

    • Type: Improvement Improvement
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 2.0-M1
    • Fix Version/s: None
    • Component/s: None
    • Labels:
      None

      Description

      The fastest way to "interface" to Groovy code seems to be to implement the getProperty method in Java code. Calling getters is a longer route in Groovy.
      For example passing "out" directly in getProperty is faster than just having the getOut method in the Groovy class.

      I have implemented a proof-of-concept implementation for TagLibraryApi and it improves the performance by several percents.

      A Groovy script could be used to generate the Java code from the getXXXX(Object instance) methods. That could be copy&pasted to the instance api implementation.
      The same type of approach could be used also for controllers (params/request is used a lot).


      Please read the comments... There is a better way using the AST transformations.

        Issue Links

          Activity

          Hide
          Lari Hotari added a comment -

          getProperty implementation of the POC.

              public Object getProperty(Object instance, String name) {
                  LazyInstanceBasedValue lazyValue = lazyInstanceBasedValuesMap.get(name);
                  if(lazyValue != null) {
                      return lazyValue.evaluate(instance);
                  }
                  Object namespaceDispatcher=lookupTagLibNamespaceDispatcher(instance, name);
                  if(namespaceDispatcher != null) {
                      return namespaceDispatcher;
                  }
                  MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(instance.getClass());
                  MetaProperty metaProperty = mc.getMetaProperty(name);
                  if(metaProperty != null) {
                      return metaProperty.getProperty(instance);
                  } else {
                      throw new MissingPropertyExceptionNoStack(name, instance.getClass());
                  }
              }
          

          Traditional code generator (groovy script) could be used to generate this (ugly, but efficient):

              private Map<String, LazyInstanceBasedValue> lazyInstanceBasedValuesMap = new HashMap<String, LazyInstanceBasedValue>();
              private void initializeLazyValues() {
                  Map<String, LazyInstanceBasedValue> m = lazyInstanceBasedValuesMap;
                  m.put("out", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getOut(instance);
                      }
                  });
                  m.put("params", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getParams(instance);
                      }
                  });
                  m.put("namespace", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getNamespace(instance);
                      }
                  });
                  m.put("pageScope", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getPageScope(instance);
                      }
                  });
                  m.put("flash", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getFlash(instance);
                      }
                  });
                  m.put("session", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getSession(instance);
                      }
                  });
                  m.put("request", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getRequest(instance);
                      }
                  });
                  m.put("webRequest", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getWebRequest(instance);
                      }
                  });
                  m.put("response", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getResponse(instance);
                      }
                  });
                  m.put("grailsApplication", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getGrailsApplication(instance);
                      }
                  });
                  m.put("pluginContextPath", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getPluginContextPath(instance);
                      }
                  });
                  m.put("controllerName", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getControllerName(instance);
                      }
                  });
                  m.put("actionName", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getActionName(instance);
                      }
                  });
                  m.put("servletContext", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getServletContext(instance);
                      }
                  });
                  m.put("grailsAttributes", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getGrailsAttributes(instance);
                      }
                  });      
                  m.put("applicationContext", new LazyInstanceBasedValue() {
                      public Object evaluate(Object instance) {
                          return getApplicationContext(instance);
                      }
                  });              
              }
          
          Show
          Lari Hotari added a comment - getProperty implementation of the POC. public Object getProperty( Object instance, String name) { LazyInstanceBasedValue lazyValue = lazyInstanceBasedValuesMap.get(name); if (lazyValue != null ) { return lazyValue.evaluate(instance); } Object namespaceDispatcher=lookupTagLibNamespaceDispatcher(instance, name); if (namespaceDispatcher != null ) { return namespaceDispatcher; } MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(instance.getClass()); MetaProperty metaProperty = mc.getMetaProperty(name); if (metaProperty != null ) { return metaProperty.getProperty(instance); } else { throw new MissingPropertyExceptionNoStack(name, instance.getClass()); } } Traditional code generator (groovy script) could be used to generate this (ugly, but efficient): private Map< String , LazyInstanceBasedValue> lazyInstanceBasedValuesMap = new HashMap< String , LazyInstanceBasedValue>(); private void initializeLazyValues() { Map< String , LazyInstanceBasedValue> m = lazyInstanceBasedValuesMap; m.put( "out" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getOut(instance); } }); m.put( "params" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getParams(instance); } }); m.put( "namespace" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getNamespace(instance); } }); m.put( "pageScope" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getPageScope(instance); } }); m.put( "flash" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getFlash(instance); } }); m.put( "session" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getSession(instance); } }); m.put( "request" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getRequest(instance); } }); m.put( "webRequest" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getWebRequest(instance); } }); m.put( "response" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getResponse(instance); } }); m.put( "grailsApplication" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getGrailsApplication(instance); } }); m.put( "pluginContextPath" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getPluginContextPath(instance); } }); m.put( "controllerName" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getControllerName(instance); } }); m.put( "actionName" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getActionName(instance); } }); m.put( "servletContext" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getServletContext(instance); } }); m.put( "grailsAttributes" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getGrailsAttributes(instance); } }); m.put( "applicationContext" , new LazyInstanceBasedValue() { public Object evaluate( Object instance) { return getApplicationContext(instance); } }); }
          Hide
          Graeme Rocher added a comment -

          I better way to do this would be to extend TagLibraryTransformer to visit all of the method call expressions and make them direct method calls for internal calls to "instance api". Similar to what this class does for GSPs:

          https://github.com/grails/grails-core/blob/master/grails-plugin-gsp/src/ast/groovy/org/codehaus/groovy/grails/compiler/web/gsp/GroovyPageOptimizerVisitor.java

          Show
          Graeme Rocher added a comment - I better way to do this would be to extend TagLibraryTransformer to visit all of the method call expressions and make them direct method calls for internal calls to "instance api". Similar to what this class does for GSPs: https://github.com/grails/grails-core/blob/master/grails-plugin-gsp/src/ast/groovy/org/codehaus/groovy/grails/compiler/web/gsp/GroovyPageOptimizerVisitor.java
          Hide
          Lari Hotari added a comment -

          Oh yes, that's a better solution. Mine was good enough for a POC.
          I'll ask Stephane if he could work on this one.

          Show
          Lari Hotari added a comment - Oh yes, that's a better solution. Mine was good enough for a POC. I'll ask Stephane if he could work on this one.
          Hide
          Raviteja added a comment -

          Pardon me but what's a POC?

          Show
          Raviteja added a comment - Pardon me but what's a POC?
          Hide
          Jeff Scott Brown added a comment -

          POC == Proof Of Concept

          Show
          Jeff Scott Brown added a comment - POC == Proof Of Concept
          Hide
          Stéphane Maldini added a comment -

          As @CompileStatic removes dynamic dispatch, it could be used as a solution for injected api instances and plain static compilation.

          Show
          Stéphane Maldini added a comment - As @CompileStatic removes dynamic dispatch, it could be used as a solution for injected api instances and plain static compilation.

            People

            • Assignee:
              Stéphane Maldini
              Reporter:
              Lari Hotari
            • Votes:
              1 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Last Reviewed:

                Development