Tuesday, November 7, 2017

Increase your JSF application performance - Version 2017

As my last posts are already 5 years old, i think it's time for a new one :D

Environment:


If you would like to use a JavaEE application server, i would go with TomEE.
It's the fastest and smallest application server available and already comes with Apache MyFaces as JSF implementation and Apache OpenWebBeans as CDI implementation.

There are already really good benchmarks available why we should choose MyFaces over Mojarra and OpenWebBeans over Weld:

Understanding JSF 2.0 Performance – Part 1
Understanding JSF 2.0 Performance – Part 2
Understanding JSF 2.0 Performance – Part 3
CDI Performance

So if you just use a plain servlet container like Tomcat or Jetty, i would also use MyFaces and OpenWebBeans.

In the past it was also a good performance boost if you use JUEL over Tomcat's EL implementation - sadly JUEL doesn't implement EL 3.0.

    Configuration:


    • Common settings:
    • <context-param>
          <param-name>javax.faces.PROJECT_STAGE</param-name>
          <param-value>Production</param-value>
      </context-param>
      <context-param>
          <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
          <param-value>-1</param-value>
      </context-param>
      <context-param>
          <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
          <param-value>server</param-value>
      </context-param>
    • Enable ViewPooling: http://lu4242.blogspot.de/2013/12/view-pooling-in-jsf-22-using-apache.html 
    • NOTE: You must also set:
      <context-param>
          <param-name>org.apache.myfaces.CACHE_EL_EXPRESSIONS</param-name>
          <param-value>alwaysRecompile</param-value>
      </context-param>
    • Enable WhiteSpace compression: http://lu4242.blogspot.de/2012/12/html-white-space-compression-for-jsf.html
    • Disable JSP support in MyFaces - this increases the startup performance and EL resolution:
    • <context-param>
          <param-name>org.apache.myfaces.SUPPORT_JSP_AND_FACES_EL</param-name>
          <param-value>false</param-value>
      </context-param>
      <!--
          NOTE: the ExpressionFactory might differ e.g. on Glassfish or Wildfly.
          This parameter is optional since MyFaces 2.3.3.
      -->
      <context-param>
          <param-name>org.apache.myfaces.EXPRESSION_FACTORY</param-name>
          <param-value>org.apache.el.ExpressionFactoryImpl</param-value>
      </context-param> 
    • Disable ManagedBeans support (since 2.3):
      <context-param>
          <param-name>org.apache.myfaces.SUPPORT_MANAGED_BEANS</param-name>
          <param-value>false</param-value>
      </context-param>
    • Reduce saved view states:
      <context-param>
          <param-name>org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION</param-name>
          <param-value>15</param-value>
      </context-param>
      <context-param>
          <param-name>org.apache.myfaces.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION</param-name>
          <param-value>3</param-value>
      </context-param> 
    • Disable ViewState compression (better performance but more memory usage):
      <context-param>
          <param-name>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</param-name>
          <param-value>false</param-value>
      </context-param>
    • Enable partial submit in PrimeFaces - this reduces the network traffic:
      <context-param>
          <param-name>primefaces.SUBMIT</param-name>
          <param-value>partial</param-value>
      </context-param>
    • Move above the fold scripts to the bottom (end of body). This is a huge improvement of the visible rendering and removes flickering between navigations (since 6.2):
      <context-param>
          <param-name>primefaces.MOVE_SCRIPTS_TO_BOTTOM</param-name>
          <param-value>true</param-value>
      </context-param>
    • Other params to increase performance:
    • <context-param>
          <param-name>org.apache.myfaces.CHECK_ID_PRODUCTION_MODE</param-name>
          <param-value>false</param-value>
      </context-param>
      <context-param>
          <param-name>org.apache.myfaces.EARLY_FLUSH_ENABLED</param-name>
          <param-value>true</param-value>
      </context-param>
    • Use a custom ServletFilter to set the correct expires/cache headers of your resources (images, stylesheets, javascripts). This is only required if you don't use the JSF resource handling for every resource. The JSF impl should already do it correctly for all JSF managed resources.
    • Compress and optimize your Javascripts in your build process. If you use maven, try primefaces-extensions' closure compiler maven plugin.
    • Enable GZIP in your webserver! If it's not supported by your webserver/container, you can still add the GzipResponseFilter from OmniFaces: http://showcase.omnifaces.org/filters/GzipResponseFilter

    Patterns:


    • Correctly define update/render and process/execute! Often this is a huge improvement as many times the whole form is updated instead only a small part. But i also prefer maintainability over performance here.
    • If you don't use ViewScoped beans, it's a good but small improvement to mark the view as stateless via transient=true.
    • Try using HTML over JSF tags.
      • Especially avoid using h:outputText if you don't need the escaping. Just use EL expressions inside your xhtml template.
      • The same applies for some other components like p:outputPanel. Just use a plain div. If you need to make it updateable, you can still use "passtrough elements" (<div jsf:id="...">...</div>)
    • Never put logic in getters because they can be called multiple times - especially for the rendered attribute!
    • Avoid logic (like inline if-statements) in EL expression! It's better to move those logic into the bean. It's faster and often easier to read and maintain.
    • Prefer AJAX over a full postback!
    • If you have many p:outputLabel's on the page and you know that the input field is required or not, it's a good performance improvemet to set the input to required=true or the outputLabel to indicateRequired=true|false. The default value for indicateRequired since 6.2 is auto, which tries to lookup BeanValidation constrains to check if @NotNull|@NotEmpty|@NotBlank are defined.
    • Cache data, which is required multiple times in the same view, in @RequestScoped beans.
    • Avoid missusing @SessionScoped, @ViewScoped and other long living scopes if not really required. 
    • Try to put only small amount of data in such long living beans. Sometimes a small flag or id of an entity is enough information. Often people put many entities in such long living beans. One of my patterns is to split beans into *Controllers (@RequestScoped) and *StateHolders (@ViewScoped, @ViewAccessScoped). *StateHolders are just data containers.

    14 comments:

    1. Hi, Thanks for the tips. Some of them are targeted for myfaces 2.3.0, which is still in beta. Do you think is safe to use it as is or a release will happen soon? (just wanted to know)

      Thanks!

      ReplyDelete
    2. I would not use the beta, we already fixed many bugs since the beta release. I think it's the best to wait 1-2 months.

      ReplyDelete
    3. Thanks for this important collected tips for better JSF performance.
      Can you advice or recommend how to measure the performance of JSF pages to compare our changes before and after to be more objective

      ReplyDelete
      Replies
      1. p:lifecycle for a single page - however the difference will not be very big. I would use jmeter ;)

        Delete
    4. I would be very curious what the current advantage is of view pooling for a seriously ajax intensive appplication. E.g. a complex form where each input is submitted, processed, validated and maybe some other fields are updated. For something I'm working on, this (in addition to some other advantages) would be the most relevant reason to switch from Mojarra 2.3.x to MyFaces 2.3.x. Does PF work 'flawlessly' with viewpooling?

      ReplyDelete
      Replies
      1. I would suggest to take a look at the linked benchmark - the biggest benefit comes from switching from mojarra to myfaces without viewpooling or stateless: http://content.jsfcentral.com/documents/35702/69490/Fog304.PNG/1784d51a-e948-4830-b46e-d3c4c19b95cb?t=1371263288000

        As you can see, viewpooling just improves the already gained perf: http://content.jsfcentral.com/documents/35702/69490/Fig306.PNG/9459636b-3a79-4820-825a-1d61b3eaf11c?t=1371263318000

        ViewPooling has only one disadvantage: PostAddToViewEvent doesn't work as before. I fixed an incompatibility with p:autoUpdate for 6.2. Everything is working now fine in my applications - also already in production.

        JFYI: ViewPooling is already available in 2.2. Currently there are only 2-3 tickets left for doing a MyFaces 2.3.0 release. I would be great if could give it a try before ;)

        Delete
      2. the benchmarks are a bit old, with version 2.2 or 2.3 things are not very different?

        Delete
      3. I dont think so. In MyFaces we always improve performance in each release. So it personally think the difference is the same or MyFaces even faster. But you can just run it again ;)

        Delete
    5. (previous 'unknown' post was made by 'kukeltje' ;-)

      ReplyDelete
    6. What do you think is a good value for compressionMinSize in Tomcat?

      ReplyDelete
    7. Thank you for the information, good content.
      Searching for Software Development Company? 3sstudio is Considered as the most well-established Software Development Company in Delhi offers you an entire range of Development services as far as ERP & CRM Development, with more than 1000+ satisfied clients.

      ReplyDelete
    8. This comment has been removed by the author.

      ReplyDelete
    9. Really helpful one. Thanks for sharing this nice one.

      Mobile App Development Services in Chennai

      ReplyDelete