Projects:Performance Review/PageSize and Caching
What and how to measure?
One point in measuring Performance is the amount of requests which are issued by a browser and the amount of data which is transferred to the browser to execute a specific action / open a specific window.
One way to capture this data is to enable the logging of each request (and the response) sent to tomcat, which can easily be done by enabling the tomcat access log. In this log a line is recorded per request which among other info records:
- requested resource (path/filename)
- response code (if an error / redirect happened)
- amount of bytes sent as a response (uncompressed)
Saving the relevant pieces of this log-output the browser behavior regarding caching or re-requesting i.e. javascript libraries can be observed and the total amount of data transferred to render one page can calculated.
The following table summarizes the results for examining the requests for the cases of an empty browser cache for executing a login an opening the product page after logging in.
empty cache #req/#bytes | primed cache #req/#bytes | |
Login | 96 / 1.378.186 | 16 / 347.453 |
Login + Product | 148 / 1.744.964 | 22 / 576.588 |
Product | 88 / 366.778 | 6 / 229.135 |
The following splits up the number for the first row which shows the login and the menu loading and the last row which represents opening the product window (edit view) for the case where the browser cache is already populated with the cacheable portions.
The data used in the login process is only loaded once during the login and the menu portion of it is loaded again on each role change.
The size of the menu as configured with the sample data and using the 'Openbravo Admin' role is about 306kb. As this default role has all access permissions nearly all available menu entries are included in this so it can be seen like some upper bound for the menu size.
The workflow which is executed for measuring the 229.135 bytes for the product page consists of opening the (empty) product page. Click ok on the automatically opened window (search/filter popup) and then loading the product page which the result (a single product in edit mode). The 6 requests needed for this are 2* the product page in edit mode, 1* the page for the search popup and 3* a dynamically generated page for requesting the potentially visible error messages translated to the currently select user interface language.
- empty product page: 104720
- populated product page: 107470
- search popup: 12475
- 3* messages: 3*1490
The main factor regarding size here is the real product page. When looking at the same flow for viewing the business partner page (empty page, search popup, page with one business partner) one gets similar sizes:
- empty business partner page: 84564
- populated business partner page: 87433
- search popup: 12578
- 3* messages: 3*1490
As a general, simplified statement a generated page is roughly 80 - 100 kilobytes in size depending on the complexity / number of fields on the page. The size of error-messages and the search popup can be omitted in comparison to loading the relevant data page two times.
In addition the look at the flow to open the product page (primed cache) shows, that no a single static resource (css,image,js-library) is requested by the browser again, after it has been stored in the browser cache. This point is important here as the number of requests to load a page with a network round-trip time is an important factor in the total time needed to transfer a page. The browser does not transfer the static data again and even does not request the data with an additional "If-modified-since" header to check if its still up to date. This browser behavior is consistent across Firefox 3 (on Linux) and Internet Explorer 7 (on Windows).
As a consequence when testing the application behavior with a tool like JMeter, all requests for static resources (css,images,js-libraries) can and should be omitted to catch the most common case of an already filled browser cache. Only when omitting these the request-pattern and server load is comparable to the real browser behavior.
This slimmed requests do not cover the case of an empty browser cache which can occur when using a clean client instance. In this case still a lot of data is requests in many requests, which make the first access to the application a lot slower than any subsequent ones.
Influence of HTTP compression and JS/CSS minimization
We did some additional measurements which show the impact of HTTP compression and JS/CSS-minimization on the size of the files as they are transferred to the client.
HTTP compression means dynamic compression of all or some files just before they are transferred to the browser. The browser uncompresses these files on the fly so that there is no user-visible change.
JS/CSS-minimization means transforming the Javascript and CSS-files before deploying the application to minimize the filesize (i.e. remove unneeded whitespace, comments, ...)
The HTTP compression can be done by two different methods. Either the servlet container i.e. tomcat) can be configured to selectively compress some or all outgoing files) or an additional apache server can be used in front of the servlet container and the compression can be moved to the apache server.
One benefit of using an additional apache server is that it can be configured to do logging per transferred file showing uncompressed size, compressed size an the compression ratio.
The following shows the results obtained by this logging for some selected (bigger) files.
filename | Uncompressed size | Compressed size | Compression ratio |
Openbravo_ERP_240.css | 240kb | 22kb | 9% |
utils.js | 147k | 29k | 19% |
dojo.js | 357k | 93k | 25% |
Empty Bpartner edit view | 83k | 10k | 11% |
The compressed sizes of this resources are significantly smaller as the original files and when applied in a bandwith limited environment should lead to a very good decrease of the response times as the transfer times decrease with the saved size.
One aspect of the dynamic output compression is added load on the server side to do the compression of the content on every request. But tests with multiple users showed no noticable impact on the tested flows on the application server so that HTTP compression should always be enabled to reduce amount of data transferred.
In addition we measured the saving gained by using static JS/CSS-minimization. The interesting point in here was if there is a gain by using the minimization on-top of the dynamic compression or not.
For the JS/CSS-minimization we use the YUI compressor tool.
The following table summarizes the gains comparing dynamic compression (only) and combined usage of minimization and dynamic compression.
filename | Uncompressed size | size (compressed) | size (minimized and compressed) |
Openbravo_ERP_240.css | 240kb | 22kb | 17k |
utils.js | 147k | 29k | 15k |
dojo.js | 357k | 93k | 83k |
This table does omit the values for the Business Partner edit view as only static not changing content is minimized. The additional savings are not as big as the ones gained by using the dynamic compression but still show some measurable improvement. As the usage of minimization comes with a one time cost at compile time only it can be easily added.
One downside of using the minimized JS- and CSS-files is that they are not easily readable anymore. But this is easily addressed by making the minimization optional and compile time, so it can be disabled.