Performance of resolving EL Value Expressions
Asked Answered
S

5

12

I have a JSF2 application that renders a large table with complex content. Unfortunately, each request takes up to 6 seconds to process. Using simple debug output inside a phase listener, I could see that the performance loss distributes evenly over all phases that process the component tree. So I started up a profiler to see what's going on in detail and found out that over 300.000 ValueExpressions are evaluated during one simple request.

They resolve to really simple getters without any logic, so the problem is not executing the code behind these expressions but parsing the expression string and invoking the getter methods. This leads to a few questions:

1.) Is there any way to speed up the resolving of method expressions. Maybe a hidden "enable caching" flag or something.

2.) It seems most of the expressions are evaluated not inside the render response phase, where they are actually needed, but during the other phases. It seems unnecessary to resolve for example styleClass during any other phase than the render phase. Can I prevent this?

3.) Of course, minimizing the number of EL expressions in my facelets page should help getting more performance, but it seems that I cannot really do this: Many attributes (like the styleClass example mentioned above) are actually dependent on the table row, but can only be set on the column. So, having 10 columns, each expression is evaluated much too often. I've seen examples where the rowClasses attribute of the table is used to conditionally style the rows, but as the table is sortable, that won't work without rolling my own sorting mechanism. Is there a better way to implement this?

4.) One more simple question: Is there a way to cache variables in the component tree (just like ui:repeat provides access to the contents of a list and resolves the expression to get the list only once, but just for one variable)?

Thank you very much for all answers and hints!

EDIT:

After further investigation, I found out that for each rendered=#{someExpression}, the expression is evaluated 6 times per row just during the render response phase. I know that JSF may call my getters more than once, but I thought this would be because they can be called inside each phase. During rendering, that values shouldn't change, so I guess they could be cached.

Stepping through the code in the debugger, it looks like javax.faces.component.ComponentStateHelper (which appears in each of the stack traces leading to the evaluated method call) provides a map to do exactly this kind of caching. However, this doesn't seem to work as I expect and always re-evaluates the expression...

Stacte answered 5/8, 2010 at 10:7 Comment(1)
did you manage to solve this somehow? I'm struggling with the same issue...Airway
R
2

1.) Is there any way to speed up the resolving of method expressions. Maybe a hidden "enable caching" flag or something.

No one comes to mind.

2.) It seems most of the expressions are evaluated not inside the render response phase, where they are actually needed, but during the other phases. It seems unnecessary to resolve for example styleClass during any other phase than the render phase. Can I prevent this?

This should as far as I know not happen. The only ones which could/should be resolved before render response are rendered, required, disabled, readonly and value.

3.) Of course, minimizing the number of EL expressions in my facelets page should help getting more performance, but it seems that I cannot really do this: Many attributes (like the styleClass example mentioned above) are actually dependent on the table row, but can only be set on the column. So, having 10 columns, each expression is evaluated much too often. I've seen examples where the rowClasses attribute of the table is used to conditionally style the rows, but as the table is sortable, that won't work without rolling my own sorting mechanism. Is there a better way to implement this?

You could hand over the styling work to a smart piece/combination of JS/CSS.

4.) One more simple question: Is there a way to cache variables in the component tree (just like ui:repeat provides access to the contents of a list and resolves the expression to get the list only once, but just for one variable)?

Use JSTL <c:set>. I am not sure how that would affect after all, but you're then basically only moving the problem to somewhere else. A #{variableName} would still have a cost of locating it in any of the scopes. You can also consider to name the scope explicitly when accessing the variable. E.g. #{sessionScope.beanname} which should skip unnecessarily scanning in page and request scopes.

Rankle answered 5/8, 2010 at 15:28 Comment(2)
Does <c:set> work in tables? I thought it would only be evaluated when the component tree ist build, not for each row in the table...?Stacte
@BlausC, can you elaborate a little more on the "scope" matter you mentioned? As I understand from my profiler output, the "search" is taking the most of the time. Is there a way to customize the order in which resolvers are called, so that I can put most used ones first? Any pointers to anything related to this is very much appreciated, I'm stuck with the same issue...Airway
C
2

I know this one is kinda old but I want to add that this issue has been solved by the MyFaces implementation. It is documented in their Wiki: https://cwiki.apache.org/confluence/display/MYFACES/Cache+EL+Expressions

Coprophagous answered 10/10, 2013 at 10:27 Comment(0)
M
1

If you are using the mojarra reference implementation on glassfish you might try a nightly build as mentioned in this blog post by Ed Burns. There were some performance improvements contributed by the oracle adf developers relating to el expression evaluation.

Not sure if this is related, but you might also try to disable partial state saving by setting the init parameter javax.faces.PARTIAL_STATE_SAVING to false.

Megrim answered 6/8, 2010 at 11:21 Comment(1)
I think your answer hints to the right direction (see my edit), but unfortunately, disabling partial state saving did not help and I am using mojarra - but on Tomcat. I did some other optimizations now that limit the performance problem to only some actions which are not performed too often but I'll definitely have a look at mojarra 2.1 when it's released (or in beta).Stacte
I
1

I suffer from repetitive getter calls on managed bean when using composite component, especially rendered getter is called million times. I do not know whether you use them too but I would like your opinion on my cacheing solution.

I succeeded with some cacheing behavior when I followed BalusC's backing component hint in the question Binding a managed bean instance to composite component.

Composite component:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:composite="http://java.sun.com/jsf/composite"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets">

<!-- INTERFACE -->
<composite:interface componentType="fieldComponentType">
    <composite:attribute name="id" type="java.lang.String" required="true" />
    <composite:attribute name="label" type="java.lang.String" required="true"/>
    <composite:attribute name="toBeRendered" type="java.lang.Boolean" required="true" />
    <composite:attribute name="currentValue" required="true" />
</composite:interface>

<!-- IMPLEMENTATION -->
<composite:implementation>
    <h:panelGrid rendered="#{cc._toBeRendered}" columns="3">
        <h:outputText value="#{cc._label}:"/>&nbsp;
        <h:inputText id="#{cc.attrs.id}" rendered="#{cc._toBeRendered}"
            value="#{cc.attrs.currentValue}" /> 
    </h:panelGrid>
</composite:implementation>
</html>    

Backing component which provides caching within one phase:

package cz.kamosh;

import javax.faces.component.FacesComponent;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;

@FacesComponent(value = "fieldComponentType")
public final class FieldComponentType extends UIComponentBase implements NamingContainer {

    static class Setting {
        String label;
        Boolean toBeRendered;

        @Override
        public String toString() {
            return "Setting [label=" + label + ", toBeRendered=" + toBeRendered
                    + "]";
        }       
    }

    int lastPhaseId = -1;

    Setting currentSetting = null;

    public FieldComponentType() {
        System.out.println("Constructor FieldComponentType");
    }   

    @Override
    public String getFamily() {
        return "javax.faces.NamingContainer";
    }

    // Must be named with prefix _, otherwise infinite loop occurs
    public String get_label() {
        Setting setting = getSetting();
        if (setting.label == null) {
            setting.label = (String) getAttributes().get("label");
        }
        return setting.label;
    }

    // Must be named with prefix _, otherwise infinite loop occurs
    public boolean is_toBeRendered() {
        Setting setting = getSetting();
        if (setting.toBeRendered == null) {
            setting.toBeRendered = (Boolean) getAttributes().get("toBeRendered");
        }
        return setting.toBeRendered;
    }

    private Setting getSetting() {
        int phaseId = FacesContext.getCurrentInstance().getCurrentPhaseId()
                .getOrdinal();

        if (currentSetting == null || phaseId > lastPhaseId) {
            currentSetting = new Setting();
            lastPhaseId = phaseId;
        }
        return currentSetting;
    }

}

Testing page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:util="http://java.sun.com/jsf/composite/components">

<h:head>
    <title>Testing page</title>
</h:head>
<h:body>

    <h:form>
        <h:panelGrid>           
            <util:fieldComponent id="id3"
                label="#{testingBean.label}"
                toBeRendered="#{testingBean.toBeRendered}"
                currentValue="#{testingBean.myValue}" />

        </h:panelGrid>
        <h:commandButton value="Do something" actionListener="#{testingBean.doSomething}" />
    </h:form>

</h:body>
</html>

Managed bean:

package cz.kamosh;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ViewScoped
@ManagedBean(name="testingBean")
public class TestingBean {

    private String myValue;

    public String getMyValue() {
        System.out.printf("getMyValue: %1$s\n", myValue);
        return myValue;
    }

    public void setMyValue(String myValue) {
        System.out.printf("setMyValue: %1$s\n", myValue);
        this.myValue = myValue;
    }

    public void doSomething() {
        System.out.printf("Do something, myValue: %1$s\n", this.myValue);
    }

    public String getLabel() {
        System.out.printf("getLabel\n");
        return "My value lbl";
    }

    public boolean isToBeRendered() {
        System.out.printf("isToBeRendered\n");
        return true;
    }

}

After being profiled using jvisualvm 26 calls of com.sun.faces.facelets.el.TagValueExpression.getValue decreased from 26% to 16% of overall time spent in one complete request 'run' (compared with composite component which does not make use of componentType="fieldComponentType" - sources not included in this answer).

But anyway overhead of JSF2 framework itself costs about 80% of time compared to 20% spent in my code even if I call some database fetches (this is my experience from our production code). And I consider this overhead quite big :-(

@FRoothowe you mentioned that "During rendering, that values shouldn't change, so I guess they could be cached." From this point of view I can afford cacheing tag expression values within each phase, am I right?

Indivertible answered 30/12, 2011 at 11:16 Comment(0)
I
1

After couple of hours of debugging I decided to improve ComponentStateHelper which is part of Mojarra JSF2 implementation. I took the last version 2.1.4 https://maven.java.net/content/repositories/releases/org/glassfish/javax.faces/2.1.4/javax.faces-2.1.4-sources.jar

The main idea is to preserve results of EL expressions evaluated during each phase. I am still assuming that result of EL expression must be the same within one phase.

Changes in class javax.faces.component.ComponentStateHelper:

class ComponentStateHelper implements StateHelper , TransientStateHelper {    

    ...

    // Own cache for method public Object eval(Serializable key, Object defaultValue) {
    int lastPhaseId = -1; // Last cached phase
    private Map<Serializable, Object> evalCache;

    ...
    public ComponentStateHelper(UIComponent component) {

        ... 
        // Instantiate own cache
        this.evalCache = new HashMap<Serializable, Object>();
    }

    ...

    /**
     * @see StateHelper#eval(java.io.Serializable, Object)
     */
    public Object eval(Serializable key, Object defaultValue) {
        Object retVal = get(key);        
        if (retVal == null) { 
            // Value evaluated and returned within one phase should be hopefully still same
            int currentPhaseId = FacesContext.getCurrentInstance().getCurrentPhaseId().getOrdinal();
            if(lastPhaseId < currentPhaseId) {
                // Probably stale cache, so clear it to get fresh results
                // in current phase
                evalCache.clear();
                lastPhaseId = currentPhaseId;
            }
            retVal = evalCache.get(key);     

            if(retVal == null) { 
                ValueExpression ve = component.getValueExpression(key.toString());
                if (ve != null) {
                    retVal = ve.getValue(component.getFacesContext().getELContext());
                }
            }
            // Remember returned value in own cache
            evalCache.put(key, retVal);         
        } 
        return ((retVal != null) ? retVal : defaultValue);
    }

    ...    
}

This enhancement seems to be functional and number of calls to my managed bean decreased drastically, especially rendered getters which were called multiple times for the same component.

I maybe do not see what disasterous consequences this enhancement may cause. But if this is feasable I wonder why JSF guys have not used this type of cacheing.

*EDIT: This solution cannot be used as it has problems when combined with DataModel!!! *

Indivertible answered 30/12, 2011 at 18:29 Comment(3)
Nice idea, but this makes it unable to let the getter return something different depending on the caller. For example, when the data is dependent on the current row data of a DataModel.Rankle
I am not sure but I thought ComponentStateHelper instance was different for different instances of rendered component (I suppose this is the caller). I will have to check this, especially for DataModel.Indivertible
@Rankle You were right, there is a problem when used eg. in h:dataTable:-( Then first evaluated value is used for all items in DataTable. In this case what is your opinion on the other answer I provided?Indivertible

© 2022 - 2024 — McMap. All rights reserved.