In profiling struts-el, I noticed a big lag in the standard taglib Resources
class in that it calls MessageFormat.format rather than caching preparsed
formats.   The initialization of a MessageFormat is easily 75% of the time
spend while the call to format() is only 25%.   I entered a bug about this
last week but have seen no response.

This is why I was happy to see that the struts' MessageResources class
utilizes a hashtable to cache the formats.   However, in studying this I was
wondering about high contention among many threads for the same format.
Would the lag actually increase if many threads had to wait for the same
format?  For example, if MessageFormat.format() took 32ms and reusing a
cached format took 8ms and 10 threads tried to use the same format
simultaneously, would this be slower since the 10th thread had to wait
10*8ms = 80ms.

Therefore, I proposed a simple pooling idea inside the hashtable and thought
you may be interested if improving performance.  It looked like the
following for the standard taglib Resources class.

    public static String getMessage(String name, Object[] a)
        throws MissingResourceException {

        // Retrieve pre-parsed format pool for message
        Stack formatPool = (Stack) formats.get(name);
        if (formatPool==null) {
            formatPool = new Stack();
            formats.put(name, formatPool);
        }

        // Retrieve format from pool or create a new one if pool
        // is empty
        MessageFormat messageFormat = null;
        try {
            messageFormat = (MessageFormat) formatPool.pop();
        }
        catch(EmptyStackException emptyx) {
            messageFormat = new MessageFormat(rb.getString(name));
        }

        // Generate formatted message
        String message = messageFormat.format(a);

        // Put parsed format back in pool for re-use by
        // another thread
        formatPool.push(messageFormat);

        return message;
    }


Note, the pooling was built upon the java Hashtable and Stack classes which
are thread save.  I have attached a diff patch for the MessageResources.java
file if you are interested.

-Nick


Index: MessageResources.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-struts/src/share/org/apache/struts/util/MessageResources.java,v
retrieving revision 1.16
diff -u -r1.16 MessageResources.java
--- MessageResources.java       9 Nov 2002 04:04:11 -0000       1.16
+++ MessageResources.java       10 Mar 2003 21:25:04 -0000
@@ -1,5 +1,5 @@
 /*
- * $Header: 
/home/cvspublic/jakarta-struts/src/share/org/apache/struts/util/MessageResources.java,v
 1.16 2002/11/09 04:04:11 dmkarr Exp $
+ * $Header: 
/home/cvs/jakarta-struts/src/share/org/apache/struts/util/MessageResources.java,v 1.16 
2002/11/09 04:04:11 dmkarr Exp $
  * $Revision: 1.16 $
  * $Date: 2002/11/09 04:04:11 $
  *
@@ -143,7 +143,7 @@
      * The set of previously created MessageFormat objects, keyed by the
      * key computed in <code>messageKey()</code>.
      */
-    protected HashMap formats = new HashMap();
+    protected Hashtable formats = new Hashtable();
 
 
     /**
@@ -327,31 +327,56 @@
      * @param args An array of replacement parameters for placeholders
      */
     public String getMessage(Locale locale, String key, Object args[]) {
+        // MessageFormats are slow to initialize, but perform
+        // the formating operation fairly quickly.  Caching the
+        // formats will result in more speed.  However, if only
+        // one format per message exists, high thread contention
+        // for the same format could result in a slowdown on a
+        // system with a heavy load.  Therefore, a simple pool
+        // of formats will allow caching of parsed formats and
+        // reduce the amount of time threads wait on each other.
+        // Note: Hashtable and Stack are thread safe
 
-       // Cache MessageFormat instances as they are accessed
+        // Cache MessageFormat instances as they are accessed
         if (locale == null)
             locale = defaultLocale;
-       MessageFormat format = null;
-       String formatKey = messageKey(locale, key);
-       synchronized (formats) {
-           format = (MessageFormat) formats.get(formatKey);
-           if (format == null) {
-               String formatString = getMessage(locale, key);
-               if (formatString == null) {
-                   if (returnNull)
-                       return (null);
-                   else
-                       return ("???" + formatKey + "???");
-               }
-               format = new MessageFormat(escape(formatString));
-               formats.put(formatKey, format);
-           }
-
-       }
-       return (format.format(args));
 
+           MessageFormat format = null;
+           String formatKey = messageKey(locale, key);
+        
+        // Retrieve pre-parsed format pool for message
+        Stack formatPool = (Stack) formats.get(formatKey);
+        if (formatPool==null) {
+            formatPool = new Stack();
+            formats.put(formatKey, formatPool);
+        }
+
+        // Retrieve format from pool or create a new one if pool
+        // is empty
+        MessageFormat messageFormat = null;
+        try {
+            messageFormat = (MessageFormat) formatPool.pop();
+        }
+        catch(EmptyStackException emptyx) {
+            String formatString = getMessage(locale, key);
+            if (formatString == null) {
+                if (returnNull)
+                    return (null);
+                else
+                    return ("???" + formatKey + "???");
+            }
+            messageFormat = new MessageFormat(escape(formatString));
+        }
+        
+        // Generate formatted message
+        String message = messageFormat.format(a);
+
+        // Put parsed format back in pool for re-use by
+        // another thread
+        formatPool.push(messageFormat);
+        
+        return message;
     }
-
 
     /**
      * Returns a text message after parametric replacement of the specified

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to