Visualforce performance tuning – lessons learned – Part I

I was optimizing a complex VF page the other day and learned a couple of tips that I did not otherwise know beforehand

Lesson I – Use apex:variable to avoid repetitive getters that always return the same value
The page used apex:repeat to create a dataTable. Each dataTable had five rows. In the edge case I was tuning, there were sixty (60) repeats – thus sixty datatables.

<!--  snip -->
<apex:repeat value="{!tableList}" var="t">
  <apex:dataTable value="{!tableRows}" var="r">
     <apex:column headerValue="column00">
       <apex:inputText value="{!r.field00}" rendered="{!isEditable}"/>
       <apex:outputText value="{!r.field00}" rendered="{!NOT(isEditable)}"/>
     </apex:column> 
  </apex:dataTable>
</apex:repeat>
<!-- snip -->

Note that the decision to render the field in column00 as input or output is based on a controller variable isEditable. This controller variable was actually a getter and had some complex logic within:

public Boolean isEditable {
  get{
      if (isEditable == null)      
         // complex logic here to set isEditable - 
         // do only once as nothing the page can do will change the decision
      return isEditable;
  }
  private set;

Looking at the debug log, when the page is constructed, this controller getter was invoked 60 * 5 * 2 times = 600 invocations! Holy moly. Even though the getter only calculates its value once, it still gets invoked for every rendered= reference on the page.

The solution is to add an apex:variable outside of the repeat to cache the value for Visualforce and thus save on all those getter invocations. Here is the new page:

<!--  snip -->
<!-- Define an apex:variable to hold the controller's getter value. 
     Use naming convention of avXXX to clue developer
     where used that it is a reference to an apex:variable -->
<apex:variable value="{!isEditable}" var="avIsEditable"/> 
<apex:repeat value="{!tableList}" var="t">
  <apex:dataTable value="{!tableRows}" var="r">
     <apex:column headerValue="column00">
       <apex:inputText value="{!r.field00}" rendered="{!avIisEditable}"/>
       <apex:outputText value="{!r.field00}" rendered="{!NOT(avIsEditable)}"/>
     </apex:column> 
  </apex:dataTable>
</apex:repeat>
<!-- snip -->

Result was a couple of tenths of seconds shaved off the page render time

Lesson 2 — Multiple controller extensions and constructor time

I had a different page with several commandButtons. Some of the buttons invoked action methods in extension00, the others invoked action methods in extension01. There was no page action.

<apex:page standardController="Opportunity" extensions="OppoExtension00,OppoExtension01">
<apex:form>
  <apex:pageBlock>
    <apex:pageBlockButtons>
      <apex:commandButton value="Invoke 00" action="{!extension00Action}"/>
      <apex:commandButton value="Invoke 01" action="{!extension01Action}"/>
    </apex:pageBlockButtons>
  </apex:pageBlock>
</apex:form>
</apex:page>

When the page is initially rendered, both controllerExtensions’ constructors are invoked. If the constructors do heavy lifting in terms of initializations (such as SOQL calls), then all of that work must finish before the page can render. If the user is only going to click on one commandButton, then the constructor time for any other commandButton using a different extension is wasted. Now, I should have known that but I was misdirected by thinking that the constructor time only applied when the commandButton was clicked and the work had to be done.

The solution was to optimize the extension’s constructors. Two techniques were applied:

  • Move SOQL out of the constructors into getters
  • Using static variables to hold common data that would be the same for both constructors

Leave a Reply

Your email address will not be published. Required fields are marked *