Table 11-6. Seconds required to read data for 128 stocks (L2 cache used)
Default configuration 61.9 seconds (33,409 SQL calls) 3.2 seconds (1 SQL call)
100.5 seconds (66,816 SQL calls) 1.19 seconds (0 SQL calls)
The first execution of this loop requires 66,816 SQL statements: 33,408 for the call to the
find() method, and an additional 33,408 for the call to the getOptions() method. Subse-
quent executions of that code are the fastest possible, since all the entities are in the L2
cache, and no SQL statements need to be issued.
WARMING UP A TEST
Java performance tests—and particularly benchmarks—usually have a warm-up period. As dis-
cussed in Chapter 4 , that allows the compiler to compile the code optimally.
Here's another example where a warm-up period is beneficial. During the warm-up period of a
JPA application, the most-frequently used entities will be loaded into the L2 cache. The measure-
ment period of the test will see very different performance as those entities are first loaded. This
is particularly true if, as in the last example, no queries are used to load entities.
Recall that in the sample database, there are five option prices for every date and symbol
pair, or a total of 167,040 option prices for 128 stocks over one year of data. When the five
stock options for a particular symbol and date are accessed via a relationship, they can all be
retrieved at once. That's why only 33,408 SQL statements are required to load all the option
price data. Even though multiple rows are returned from those SQL statements, JPA is still
able to cache the entities—it is not the same thing as executing a query. If the L2 cache is
warmed up by iterating through entities, don't iterate through related entities individu-
ally—do that by simply visiting the relationship.
As code is optimized, you must take into account the effects of the cache (and particularly
the L2 cache). Even if you think you could write better SQL than what JPA generates (and
hence should use complex named queries), make sure that code is worthwhile once the cache
comes into play. Even if it seems that using a simple named query will be faster to load data,
consider what would happen in the long run if those entities were loaded into the L2 cache
via a call to the find() method.