I think performance improvements in Forge 1.x are very important even now!<br><br>It&#39;s going to be around for a very long time and while we are working on Forge 2.0 now, the kinds of improvements you are talking about, Thomas, are very significant!<br>
<br>How can we help? Do you think you could put this code into a branch with a single commit (or just one for each change) so that we can see just how much you had to change?<br><br>The problem with the weld container is the Hibernate Tools plugin. If that one works, and the Openshift and AS7 plugins work, then it&#39;s safe to assume that it&#39;s working fine. It was a hack to solve a classloading problem, but if we can fix it now, that&#39;s great!<br>
<br>~Lincoln<br><div class="gmail_extra"><br><br><div class="gmail_quote">On Sat, Nov 3, 2012 at 3:27 PM, George Gastaldi <span dir="ltr">&lt;<a href="mailto:ggastald@redhat.com" target="_blank">ggastald@redhat.com</a>&gt;</span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi Thomas,<br>
<br>
Nice to know that you&#39;ve found some improvements for Forge 1.x startup time. However, we are rewriting Forge in 2.x from the ground up and we&#39;re focusing on performance and flexibility in first place.<br>
<br>
We encourage you to try the 2.0 branch and help us on finding some possible performance problems if you feel so.<br>
<br>
As for now, we are avoiding any major changes and updates to 1.x.<br>
<br>
Best Regards,<br>
<br>
George Gastaldi<br>
<br>
Em 03/11/2012, às 15:41, Thomas Frühbeck &lt;<a href="mailto:fruehbeck@aon.at">fruehbeck@aon.at</a>&gt; escreveu:<br>
<div class="HOEnZb"><div class="h5"><br>
&gt; Hi,<br>
&gt;<br>
&gt; following the discussions about boot-up performance of Forge I made some<br>
&gt; investigations using a profiler.<br>
&gt; After testing a lot of different scenarios I would like to share my<br>
&gt; results and ask for your opinion on it.<br>
&gt;<br>
&gt; *************************************************************************************<br>
&gt; Issue 1.: hot spot inv.<br>
&gt; org.jboss.forge.bus.cdi.ObserverCaptureExtension.scan<br>
&gt;     profiling shows that 37% CPU time are used in firing<br>
&gt; ProcessAnnotatedType events, which are AFAICS only used to filter old<br>
&gt; event types<br>
&gt;     &quot;37,2% - 13.442 ms - 1.866 inv.<br>
&gt; org.jboss.weld.bootstrap.events.ProcessAnnotatedTypeImpl.fire&quot;<br>
&gt;<br>
&gt;     By introducing a preparatory check in ObserverCaptureExtension like<br>
&gt;&gt;   if (isMethodsAnnotated(originalType, Observes.class)) {<br>
&gt;&gt;   ....<br>
&gt;&gt;   }<br>
&gt;<br>
&gt; and something like:<br>
&gt;&gt;  private boolean isMethodsAnnotated(AnnotatedType&lt;?&gt; type, Class&lt;?<br>
&gt; extends Annotation&gt; annotation) {<br>
&gt;&gt;      if (type.getClass().isAnnotationPresent(annotation))<br>
&gt;&gt;         return true;<br>
&gt;&gt;      for (Method m : type.getClass().getMethods()) {<br>
&gt;&gt;          if (m.isAnnotationPresent(annotation))<br>
&gt;&gt;              return true;<br>
&gt;&gt;          for (Annotation[] arr : m.getParameterAnnotations()) {<br>
&gt;&gt;             for (Annotation a : arr) {<br>
&gt;&gt;                 if (a.equals(annotation))<br>
&gt;&gt;                     return true;<br>
&gt;&gt;             }<br>
&gt;&gt;          }<br>
&gt;&gt;      }<br>
&gt;&gt;      return false;<br>
&gt;&gt;  }<br>
&gt;<br>
&gt;     we could reduce startup by 20%<br>
&gt;     &quot;13,8% - 2.438 ms - 1.798 inv.<br>
&gt; org.jboss.weld.bootstrap.events.ProcessAnnotatedTypeImpl.fire<br>
&gt;<br>
&gt; Test: call Forge with commands &quot;list-plugins&quot; and &quot;exit&quot;, like ~&gt; time<br>
&gt; ((echo forge list-plugins; echo exit;)| bin/forge)<br>
&gt;<br>
&gt; Before optimization:<br>
&gt; ~/forge&gt; time ((echo forge list-plugins &amp;&amp; echo exit)| bin/forge )<br>
&gt;     _____<br>
&gt;    |  ___|__  _ __ __ _  ___<br>
&gt;    | |_ / _ \| `__/ _` |/ _ \  \\<br>
&gt;    |  _| (_) | | | (_| |  __/  //<br>
&gt;    |_|  \___/|_|  \__, |\___|<br>
&gt;                    |___/<br>
&gt;<br>
&gt; [no project] forge-distribution-1.1.1-SNAPSHOT $ forge list-plugins<br>
&gt; org.richfaces.forge.richfaces-forge-plugin:1.0.5.Final:1.0.0-SNAPSHOT-b1a6ebed-462f-40e1-ac08-82f927489301<br>
&gt; com.ocpsoft.forge.prettyfaces-plugin:1.0.2.Final:1.0.0-SNAPSHOT-c6b071ad-a6df-4112-8fff-82ecccd003fb<br>
&gt; org.jboss.hibernate.forge.hibernate-tools-plugin:1.0.5.Final:1.0.0-SNAPSHOT-afb1da29-a99f-4dd7-b020-5f3ba6073cde<br>
&gt; org.arquillian.forge.arquillian-plugin:1.0.3-SNAPSHOT:1.0.0-SNAPSHOT-42907982-d02e-4a1d-8ea5-8e66c8013fde<br>
&gt; [no project] forge-distribution-1.1.1-SNAPSHOT $ exit<br>
&gt;<br>
&gt;<br>
&gt; real    0m9.721s<br>
&gt; user    0m26.533s<br>
&gt; sys     0m0.433s<br>
&gt;<br>
&gt; After optimization:<br>
&gt; ~forge1.1.1-weld1.2&gt; time ((echo forge list-plugins; echo exit;)| bin/forge)<br>
&gt; Using Forge at /raid/home/thomas/java/forge1.1.1-weld1.2<br>
&gt;     _____<br>
&gt;    |  ___|__  _ __ __ _  ___<br>
&gt;    | |_ / _ \| `__/ _` |/ _ \  \\<br>
&gt;    |  _| (_) | | | (_| |  __/  //<br>
&gt;    |_|  \___/|_|  \__, |\___|<br>
&gt;                    |___/<br>
&gt;<br>
&gt; [no project] forge1.1.1-weld1.2 $ forge list-plugins<br>
&gt; org.richfaces.forge.richfaces-forge-plugin:1.0.5.Final:1.0.0-SNAPSHOT-b1a6ebed-462f-40e1-ac08-82f927489301<br>
&gt; com.ocpsoft.forge.prettyfaces-plugin:1.0.2.Final:1.0.0-SNAPSHOT-c6b071ad-a6df-4112-8fff-82ecccd003fb<br>
&gt; org.jboss.hibernate.forge.hibernate-tools-plugin:1.0.5.Final:1.0.0-SNAPSHOT-afb1da29-a99f-4dd7-b020-5f3ba6073cde<br>
&gt; org.arquillian.forge.arquillian-plugin:1.0.3-SNAPSHOT:1.0.0-SNAPSHOT-42907982-d02e-4a1d-8ea5-8e66c8013fde<br>
&gt; [no project] forge1.1.1-weld1.2 $ exit<br>
&gt;<br>
&gt;<br>
&gt; real    0m6.016s<br>
&gt; user    0m14.482s<br>
&gt; sys     0m0.449s<br>
&gt;<br>
&gt;<br>
&gt; *************************************************************************************<br>
&gt; Issue 2.: double starting of Weld container<br>
&gt; I admit that I do not fully understand, what I did and if this really<br>
&gt; provides the full solution, but it worked and produced a fully<br>
&gt; functional Forge instance.<br>
&gt;<br>
&gt; I tried to restructure the Bootstrap logic a bit:<br>
&gt;<br>
&gt;                initializeClassloader();<br>
&gt;                WeldContainer container = weld.initialize();<br>
&gt;                manager = container.getBeanManager();<br>
&gt;<br>
&gt;                try<br>
&gt;                {<br>
&gt;                   loadPlugins();<br>
&gt;                   weld.reInitialize();<br>
&gt;<br>
&gt; where initializeClassloader already uses the CompositeClassloader:<br>
&gt;<br>
&gt;    private static void initializeClassloader() {<br>
&gt;        ModuleLoader moduleLoader = Module.getBootModuleLoader();<br>
&gt;<br>
&gt;        CompositeClassLoader composite = new CompositeClassLoader();<br>
&gt; composite.add(Module.forClassLoader(Bootstrap.class.getClassLoader(),<br>
&gt; true).getClassLoader());<br>
&gt;<br>
&gt;        Thread.currentThread().setContextClassLoader(composite);<br>
&gt;    }<br>
&gt;<br>
&gt; and loadPlugins simply adds more classloaders:<br>
&gt;<br>
&gt;          CompositeClassLoader composite =<br>
&gt; (CompositeClassLoader)Thread.currentThread().getContextClassLoader();<br>
&gt;          for (PluginEntry plugin : toLoad)<br>
&gt;          {<br>
&gt;                Module module =<br>
&gt; moduleLoader.loadModule(ModuleIdentifier.fromString(plugin.toModuleId()));<br>
&gt;                composite.add(module.getClassLoader());<br>
&gt;<br>
&gt;<br>
&gt; This way the classes are already available in the correct classloader<br>
&gt; for the later ModularWeld.reInitialize():<br>
&gt;<br>
&gt;     public void reInitialize() {<br>
&gt;         bootstrap.revisit();<br>
&gt;         bootstrap.validateBeans();<br>
&gt;         bootstrap.endInitialization();<br>
&gt;     }<br>
&gt;<br>
&gt; Where Bootstrap.revisit simply revisits the BeanDeployments which have<br>
&gt; been newly found:<br>
&gt;<br>
&gt;     private static class DeploymentVisitor {<br>
&gt; .....<br>
&gt;         public Map&lt;BeanDeploymentArchive, BeanDeployment&gt; revisit() {<br>
&gt;             for (BeanDeploymentArchive archvive :<br>
&gt; deployment.getBeanDeploymentArchives()) {<br>
&gt;                 if<br>
&gt; (!managerAwareBeanDeploymentArchives.containsKey(archvive)) {<br>
&gt;                     visit(archvive, managerAwareBeanDeploymentArchives,<br>
&gt; new HashSet&lt;BeanDeploymentArchive&gt;(), true);<br>
&gt;                     BeanDeployment bd =<br>
&gt; managerAwareBeanDeploymentArchives.get(archvive);<br>
&gt;                     bd.createBeans(environment);<br>
&gt;                     bd.deployBeans(environment);<br>
&gt;                 }<br>
&gt;             }<br>
&gt;             return managerAwareBeanDeploymentArchives;<br>
&gt;         }<br>
&gt;<br>
&gt;<br>
&gt; *************************************************************************************<br>
&gt; Issue 3.: invocation of potentially expensive operations on<br>
&gt; non-annotated or annotation-relevant classes<br>
&gt; The current BeanDeployer simply invokes ProcessAnnotatedType on any<br>
&gt; class, w/o checking, if this class is used by any annotation anywhere.<br>
&gt; By jandexing all jars and assembling the dispersed Jandex indices during<br>
&gt; URLScanning we could reduce the expensive WeldClass conversion.<br>
&gt;<br>
&gt; Original:<br>
&gt;     11.108 ms - 2.024 inv. org.jboss.weld.bootstrap.BeanDeployer.addClass<br>
&gt; Jandex-aware:<br>
&gt;     4.721 ms - 990 inv. org.jboss.weld.bootstrap.BeanDeployer.addClass<br>
&gt;<br>
&gt; Possible solution:<br>
&gt;     - 1. URLHandler scans Jandex file locations<br>
&gt;<br>
&gt;     protected void addToDiscovered(String name, URL url) {<br>
&gt; ...<br>
&gt;         } else if (name.endsWith(JANDEX_IDX)) {<br>
&gt;             discoveredJandexIndexUrls.add(url);<br>
&gt;         }<br>
&gt;     }<br>
&gt;<br>
&gt;     - 2. URLScanner assembles all found Jandex indices:<br>
&gt;<br>
&gt;         List&lt;Index&gt; jandexIndexes = new ArrayList&lt;Index&gt;();<br>
&gt;         for (URL jandexUrl : handler.getDiscoveredJandexIndexUrls()) {<br>
&gt;                 jandexIndexes.add(new<br>
&gt; IndexReader(jandexUrl.openStream()).read());<br>
&gt;         }<br>
&gt;<br>
&gt;     - 3. BeanDeployer does expensive WeldClass loading only if class is<br>
&gt; mentioned in the Jandex indices<br>
&gt;     - 3.a. first collapse the Jandex-indices to a simple list of class<br>
&gt; names:<br>
&gt;         for (Index jandexIndex : jandexIndexes) {<br>
&gt;             for (List&lt;AnnotationInstance&gt; aiList :<br>
&gt; jandexIndex.getAnnotations().values()) {<br>
&gt;                 for (AnnotationInstance ai : aiList) {<br>
&gt;                     AnnotationTarget at = ai.target();<br>
&gt;                     if (at instanceof ClassInfo) {<br>
&gt; jandexAnnotated.add(((ClassInfo)at).name().toString());<br>
&gt;                     } else if (at instanceof MethodInfo) {<br>
&gt; jandexAnnotated.add(((MethodInfo)at).declaringClass().name().toString());<br>
&gt;                     } else if (at instanceof MethodParameterInfo) {<br>
&gt; jandexAnnotated.add(((MethodParameterInfo)at).method().declaringClass().name().toString());<br>
&gt;                     } else if (at instanceof FieldInfo) {<br>
&gt; jandexAnnotated.add(((FieldInfo)at).declaringClass().name().toString());<br>
&gt;                     }<br>
&gt;                 }<br>
&gt;             }<br>
&gt;         }<br>
&gt;<br>
&gt;     - 3.b. query the aggregated list of Jandex-aware classes<br>
&gt;<br>
&gt;     public BeanDeployer addClass(String className) {<br>
&gt;         Class&lt;?&gt; clazz = loadClass(className);<br>
&gt;         if (clazz != null &amp;&amp; isBeanCandidate(clazz) &amp;&amp;<br>
&gt; jandexContains(className)) {<br>
&gt;             WeldClass&lt;?&gt; weldClass = loadWeldClass(clazz);<br>
&gt;<br>
&gt;<br>
&gt; Yet unsolved issue is the question, wheter an index is found in the<br>
&gt; corresponding Archive or not, so not to enforce Jandexing _all_ jars for<br>
&gt; Forge.<br>
&gt; Jandexing is very easy though: jandex -m &lt;jar&gt;<br>
&gt;<br>
&gt; The abovementioned solutions have been implemented and produced a<br>
&gt; functional and correctly working Forge instance.<br>
&gt; AFAICS the solution for issue 2 could be a means to solve the reloading<br>
&gt; of plugins too, but I didn&#39;t implement and verify this.<br>
&gt;<br>
&gt; The sources are not available online, because the current implementation<br>
&gt; is highly fragile and not safe for simple usage - too many changes in<br>
&gt; APIs, dependencies, etc.<br>
&gt;<br>
&gt; Comments welcome!<br>
&gt;<br>
&gt; Regards,<br>
&gt; Thomas<br>
&gt;<br>
&gt; _______________________________________________<br>
&gt; forge-dev mailing list<br>
&gt; <a href="mailto:forge-dev@lists.jboss.org">forge-dev@lists.jboss.org</a><br>
&gt; <a href="https://lists.jboss.org/mailman/listinfo/forge-dev" target="_blank">https://lists.jboss.org/mailman/listinfo/forge-dev</a><br>
<br>
_______________________________________________<br>
forge-dev mailing list<br>
<a href="mailto:forge-dev@lists.jboss.org">forge-dev@lists.jboss.org</a><br>
<a href="https://lists.jboss.org/mailman/listinfo/forge-dev" target="_blank">https://lists.jboss.org/mailman/listinfo/forge-dev</a><br>
</div></div></blockquote></div><br><br clear="all"><br>-- <br>Lincoln Baxter, III<br><a href="http://ocpsoft.org" target="_blank">http://ocpsoft.org</a><br>&quot;Simpler is better.&quot;<br>
</div>