I am taking a stab at profiling Velocity using Jinsight (from IBM alphaWorks).

For a test case, I basically turned Jon's speed test servlet into a 
standalone program by liberally cutting and pasting from 
Example.java. Output goes to a StringWriter. I call the whole mess 
twice so the second time Velocity.init() should be fast and all the 
templates should be cached. I am focusing on the second-time 
performance.

I have some preliminary results and some questions.

- The big news is that nothing jumps out as a major time sink. This 
is good because it means there are no major bloopers, but bad because 
there is nothing to cherry pick.

- ASTReference.render() is called a lot (I guess that's no surprise). 
It builds its output by using String concatenation, for example
   writer.write(NodeUtils.specialText(getFirstToken()) + prefix + 
value.toString());

This creates a temp StringBuffer, appends three strings to it and 
converts using toString().

Changing that one line to the three separate writes
             writer.write(NodeUtils.specialText(getFirstToken()));
             writer.write(prefix);
             writer.write(value.toString());

saves about 17% of the time spent in this method. All the other uses 
of + in this method can be changed too.

This assumes that writes are fast, for StringWriter they are, 
probably for BufferedWriter too.

Some minor improvements:

- In Parse.render(), the check of the template stack depth is pretty 
expensive. It takes about 20% of the time of the whole method! The 
time is split between 
InternalContextAdapterImpl.getTemplateNameStack() and 
Runtime.getInt(). I rewrote Configuration.getInteger() to avoid 
creating an Integer and that helps a little.

- ResourceManager.getResource() calls 
globalCache.containsKey(resourceName) followed by globalCache.get() 
if the containsKey succeeds. I replaced these with just a get().

OK, those are the easy ones. Here are some other possibilities:

- About 40% of the time is spent in ASTReference.execute(), any 
improvements here could have a big effect. One thing that stands out 
here is the overhead of layering through InternalContextAdapter -> 
AbstractContext -> VelocityContext -> HashMap to actually get a value 
or a method.

- I am seeing a few places where caching a value in a node could be 
beneficial. For example, ASTNumberLiteral.value() parses its integer 
value on each call. This is a relatively expensive operation. Another 
example is ASTSetDirective.render() which calls String.substring() to 
get the identifier name.

I know that caching can be problematic in multi-threaded code, so I'm 
not comfortable caching a value in a node at render time. I was 
wondering if there is any time during parsing where the cached value 
can be computed? It would have to be after the child nodes have been 
added.

ASTNumberLiteral could cache the integer value, that at least avoids 
the issues with cached references. Can anyone tell me if this code is 
thread-safe? Or how to make it thread-safe?

     private boolean cached = false;
     private int cachedValue;

     public Object value( InternalContextAdapter context)
     {
        if (!cached) {
                cachedValue = Integer.parseInt(getFirstToken().image);
                cached = true;
        }
         return new Integer(cachedValue);
     }


Finally, here are diffs for ASTReferenceNode.java, Configuration.java 
and ResourceManager.java.

Kent Johnson                   Transparent Language, Inc.
[EMAIL PROTECTED]       http://www.transparent.com


--- d:/dev/jakarta-velocity/old/ASTReference.java   Wed Apr 18 12:03:15 2001
+++ ASTReference.java   Wed Apr 18 14:51:45 2001
@@ -215,10 +215,19 @@
          if ( escaped )
          {
              if ( value == null )
-                writer.write( NodeUtils.specialText(getFirstToken()) 
+ prefix + "\\" +  nullString );
+            {
+                writer.write( NodeUtils.specialText(getFirstToken()) );
+                writer.write( prefix );
+                writer.write( "\\" );
+                writer.write( nullString );
+            }
              else
-                writer.write( NodeUtils.specialText(getFirstToken()) 
+ prefix + nullString );
-
+            {
+                writer.write( NodeUtils.specialText(getFirstToken()) );
+                writer.write( prefix );
+                writer.write( nullString );
+            }
+
              return true;
          }

@@ -232,14 +241,19 @@
               *  write prefix twice, because it's shmoo, so the \ 
don't escape each other...
               */

-            writer.write(NodeUtils.specialText(getFirstToken()) + 
prefix + prefix + nullString);
+           writer.write( NodeUtils.specialText(getFirstToken()) );
+           writer.write( prefix );
+           writer.write( prefix );
+           writer.write( nullString );

              if (referenceType != QUIET_REFERENCE && 
Runtime.getBoolean( 
RuntimeConstants.RUNTIME_LOG_REFERENCE_LOG_INVALID, true) )
                  Runtime.warn(new ReferenceException("reference : 
template = " + context.getCurrentTemplateName(), this));
          }
          else
          {
-            writer.write(NodeUtils.specialText(getFirstToken()) + 
prefix + value.toString());
+            writer.write(NodeUtils.specialText(getFirstToken()));
+            writer.write(prefix);
+            writer.write(value.toString());
          }

          return true;


--- d:/dev/jakarta-velocity/old/Configuration.java  Wed Apr 18 13:52:53 2001
+++ Configuration.java  Wed Apr 18 13:58:05 2001
@@ -1485,7 +1485,10 @@
      public int getInteger(String key,
                            int defaultValue)
      {
-        return getInteger(key, new Integer(defaultValue)).intValue();
+        Integer i = getInteger(key, null);
+        if (i == null)
+           return defaultValue;
+        return i.intValue();
      }

      /**



--- d:/dev/jakarta-velocity/old/ResourceManager.java    Wed Apr 18 
14:19:48 2001
+++ ResourceManager.java    Wed Apr 18 14:23:07 2001
@@ -218,8 +218,6 @@
      public static Resource getResource(String resourceName, int resourceType)
          throws ResourceNotFoundException, ParseErrorException, Exception
      {
-        Resource resource = null;
-
          /*
           * Check to see if the resource was placed in the cache.
           * If it was placed in the cache then we will use
@@ -227,10 +225,9 @@
           * will load it.
           */

-        if (globalCache.containsKey(resourceName))
+        Resource resource = (Resource) globalCache.get(resourceName);
+        if (resource != null)
          {
-            resource = (Resource) globalCache.get(resourceName);
-
              /*
               * The resource knows whether it needs to be checked
               * or not, and the resource's loader can check to

Reply via email to