This is now solved.<br><br>I noticed that the problem became worse the bigger the classpath was, even if the extra jars contained totally irrellevant classes.<br><br>Profiling revealed that the rule compilation generated an enomous amount of IO traffic from ClassLoader.loadClass originating from <br>
org.mvel.ParserContext.checkForDynamicImport and org.drools.rule.PackageCompilationData$PackageClassLoader.<br><br>In both these places Drools generates a lot of loadClass calls for classes that does not exist.<br>It seems like the Sun classloaders does not cache the result of failed class lookups, but scans the whole classpath again and again looking for these non-existing classes.<br>
That is why the problem gets bigger the larger your classpath is. Our classpath contains 100+ jars, so we got hit pretty bad.<br><br>I solved the problem by creating a caching classloader that also caches the result of failed lookups. When I added this as the context classloader scoped around all my compilation I saw a performance increase of a factor of 10 - 20 in our various environments.<br>
<br>Here are the stats from the caching classloader after compiling 850 rules:<br><br>Caching loader called 200608 times<br>119 actual successful loads<br>
932 actual failed loads<br>73738 cache hits for successfull loads<br>125819 cache hits for failed loads<br><br>In other words: Drools generated 126751 loads for 932 non-existing classes. We saved 125819 full classapth scans by caching the failed lookups.<br>
<br>As mentioned above the caching classloader can be hooked into the system by setting it at the context classloader.<br>org.mvel.ParserContext.checkForDynamicImport uses the context classloader directly.<br>org.drools.rule.PackageCompilationData$PackageClassLoader uses the classloader configured in PackageBuilderConfiguration which is the context classloader by default.<br>
This means you get the full effect only when you scope the caching class loader around all your PackageBuilderConfiguration and PackageBuilder usage.<br><br>Below is the code for our caching classloader for the reference of anybody else who might encounter this problem.<br>
<br>Regards<br>Chr<br><br>PS Edson:<br>This problem is related to compilation. The RETE building is usually very fast.<br><br>----------------------------------------------<br>
package domain.rules;<br><br>import java.util.HashMap;<br>import java.util.Map;<br><br>/**<br> * Classloader that intercepts calls to the current context class loader and caches the result of the calls to {@link #loadClass}<br>
* on it.<br> * <p/><br> * The interception will start automagically when this class is instantiated. Make sure that you call {@link #stop()} from a finally<br> * block when you want the loader interception to stop, to avoid unnecessary caching.<br>
*<br> * @author Christian Nedregård<br> * @see Thread#getContextClassLoader()<br> */<br>public class CachingContextClassLoader extends ClassLoader {<br> private Map classLoaderResultMap = new HashMap();<br> private ClassLoader originalClassLoader;<br>
private int successfulCalls = 0;<br> private int failedCalls = 0;<br> private int cacheHits = 0;<br> private int successfulLoads = 0;<br> private int failedLoads = 0;<br><br> /**<br> * Create an instance. The instance will automagically start intercepting {@link #loadClass(String)} calls to the current<br>
* context class loader.<br> *<br> * @see Thread#getContextClassLoader()<br> */<br> CachingContextClassLoader() {<br> super(Thread.currentThread().getContextClassLoader());<br> this.originalClassLoader = Thread.currentThread().getContextClassLoader();<br>
Thread.currentThread().setContextClassLoader(this);<br> }<br><br> /**<br> * Try to load the class from the original context class loader and chache the result (also <code>ClassNotFoundException</code>s).<br>
* Subsequent calls for the same class name will return the cached version, or throw a cached ClassNotFoundException if it was<br> * not found initially.<br> *<br> * @param name the name of the class to find.<br>
* @return the cached result of a call to <code>loadClass</code> on the original context class loader.<br> * @throws ClassNotFoundException when the class of the given name could not be found.<br> */<br>
public Class loadClass(String name) throws ClassNotFoundException {<br> Object result;<br> if (classLoaderResultMap.containsKey(name)) {<br> ++cacheHits;<br> result = classLoaderResultMap.get(name);<br>
} else {<br> try {<br> result = super.loadClass(name);<br> ++successfulLoads;<br> } catch (ClassNotFoundException e) {<br> ++failedLoads;<br> result = e;<br>
}<br> classLoaderResultMap.put(name, result);<br> }<br> if (result instanceof ClassNotFoundException) {<br> ++failedCalls;<br> throw (ClassNotFoundException) result;<br>
} else {<br> ++successfulCalls;<br> return (Class) result;<br> }<br> }<br><br> /**<br> * Stop intercepting by resetting the original context classloader using {@link Thread#setContextClassLoader(ClassLoader)}.<br>
*/<br> public void stop() {<br> Thread.currentThread().setContextClassLoader(originalClassLoader);<br> }<br><br> /**<br> * Get a textual report of the caching that was performed.<br> *<br> * @return a textual report of the caching that was performed.<br>
*/<br> public String getReport() {<br> return new StringBuffer("Loader called ").append(successfulCalls + failedCalls).append(" times, ").append(successfulCalls)<br> .append(" successfull, ").append(failedCalls).append(" failed. Cache hits: ").append(cacheHits)<br>
.append(", successful loads: ").append(successfulLoads).append(", Failed loads: ").append(failedLoads)<br> .append('.').toString();<br> }<br>}<br><br>----------------------------------------------<br>
<br><br><div class="gmail_quote">2009/4/17 Edson Tirelli <span dir="ltr"><<a href="mailto:tirelli@post.com">tirelli@post.com</a>></span><br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<br> Christian,<br><br> Can you break down this performance into 2 steps?<br><br>1. COMPILE: this is the step where textual information is parsed and compiled into binary data. This is done by the PackageBuilder.addPackageFromDrl() method.<br>
<br>2. RETE BUILDING: this is the step where the binary data is assembled into the rete network. This is done by RuleBase.addPackage() method.<br><br> These two processes are mostly independent and it would be good to know what is the relative time spent in each.<br>
<br> After that, we will need to get a few rule samples from you with a test case so that we can investigate. <br><br> Thanks,<br> Edson<br><br><div class="gmail_quote">2009/4/17 Christian Nedregård <span dir="ltr"><<a href="mailto:christian_nedregaard@email.com" target="_blank">christian_nedregaard@email.com</a>></span><div>
<div></div><div class="h5"><br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">Thank you for the quick reply and the tip Ingomar.<br><br>Since our rules are template based I was able to merge them into 30 drls (850 before). This gave a 30% perfomance improvement, but we are still not able to compile more than 2 rules per second. I also tried to execute in with jdk 1.6.0_03 instead of 1.4.2_11. This only gave a small performance increase.<br>
<br>We are also seing the same slow performance in our production environment wich is JDK 1.4.2_11 on Solaris, so this is not a OS spesific problem.<br><br>I addition I tried to run the compilation in a dedicated process with 1G available memory with no performance increase, so it is not related to memory restrictions either.<br>
<br>From our experimentation I see that the compile time, unsurpisingly, is directly connectet to the complexity of the rule. If i strip away he LHS and RHS of the rules and only keep the imports and global definitions I am able to get a compilation throughput at 24 rules per second.<br>
<br>Are your rules much simpler than ours? Can you think of any other reason why you are seeing a much better compiler throughput?<br><br>Regards<br>Chr<div><div></div><div><br><br><br><div class="gmail_quote">
On Fri, Apr 17, 2009 at 10:34 AM, Ingomar Otter <span dir="ltr"><<a href="mailto:iotter@mac.com" target="_blank">iotter@mac.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">Christian,<br>
with our app, we see a compile performance which is at least an order of magnitude higher.<br>
I would assume that in your case the extreme fine granularity of your DRLs may be slowing things down.<br>
Have you tried to consolidate these in bigger but fewer DRls?<br>
<br>
If you need that granualarity, you still have the option of joining these together before running it with the help of perl and the likes.<br>
<br>
--I<br>
_______________________________________________<br>
rules-users mailing list<br>
<a href="mailto:rules-users@lists.jboss.org" target="_blank">rules-users@lists.jboss.org</a><br>
<a href="https://lists.jboss.org/mailman/listinfo/rules-users" target="_blank">https://lists.jboss.org/mailman/listinfo/rules-users</a><br>
<br>
</blockquote></div><br>
</div></div><br>_______________________________________________<br>
rules-users mailing list<br>
<a href="mailto:rules-users@lists.jboss.org" target="_blank">rules-users@lists.jboss.org</a><br>
<a href="https://lists.jboss.org/mailman/listinfo/rules-users" target="_blank">https://lists.jboss.org/mailman/listinfo/rules-users</a><br>
<br></blockquote></div></div></div><font color="#888888"><br><br clear="all"><br>-- <br> Edson Tirelli<br> JBoss Drools Core Development<br> JBoss, a division of Red Hat @ <a href="http://www.jboss.com" target="_blank">www.jboss.com</a><br>
</font><br>_______________________________________________<br>
rules-users mailing list<br>
<a href="mailto:rules-users@lists.jboss.org">rules-users@lists.jboss.org</a><br>
<a href="https://lists.jboss.org/mailman/listinfo/rules-users" target="_blank">https://lists.jboss.org/mailman/listinfo/rules-users</a><br>
<br></blockquote></div><br>