Field specialization applies to hotspots at the routine level and sometimes even to short code segments within a routine. As an example, field specialization could be applied to a particular hot loop, minimizing the code changes required. Further, specialization can be applied individually to specific hotspots within the same function, or to many such hotspots collectively. Such flexibility results in small specializations and enables field specializations to be applied rapidly and aggressively.
Let’s now look at a very simple example illustrating the spiff annotations. The annotations are colored blue.This particular specialization produces code specialized on the number of columns.
First of all, when a function is chosen to be specialized, we annotate it with #pragma tcg snippet and #pragma tcg end snippet. The keyword tcg represents template code generate. This is to inform the SRE that the enclosed snippet is a template for generating specialized code. Subsequently, we provide a name used to generate the identifier for the specialized code. In this example, we use the name of the function for simplicity. Then we provide the pattern parameter identifier (NUM_COLUMNS).
A pattern parameter is used to substitute a variable or an expression within the function source code, such that the actual value of the variable or expression can be placed into the template code directly for code generation. Depending on the availability of the pattern parameter value, i.e., at compile time or at runtime, the specialized code (termed speccode) can be generated statically or dynamically.
Second, we show two simple examples of speccode generation.
For static speccode generation, the developer can simply specify the following directive in the source file, preferably the same one where the routine that is specialized is located. In this case, we can put the following statement right after the tcg end snippet.
This above directive will notify the SRE to create a version of the SequentialScan() routine at compile time with NUM_COLUMNS substituted by 7.
To perform dynamic code generation, we will use a similar directive to specify the spiff. In addition, we need to create a routine that can be invoked at runtime to generate the speccode. (It is good form to put all the speccode generators in a separate, short source file.)
This routine can be invoked, for instance, from the routine that creates tables, where the number of columns in the newly-created table can be obtained.
int DefineTable(TableSchema const *schema)
// Table creation logic
// At this point, the table is successfully created.
// We call the speccode generator to produce the SeqScan speccode
// that is tailored to this particular table.
The routine above is executed at runtime by the DBMS, when the user issue a CREATE TABLE statement. The resulting speccode will be stored both on disk and in memory. The in-memory speccode can be executed immediately without restarting the DBMS; the on-disk version will be loaded when the DBMS restarts.
In fact, the DBMS should load both statically- and dynamically-generated speccodes at start-up time. A convenient choice to do so is in the initialization routine of the DBMS.
// Database start up logic
// At the point, the database should be up and running. Time to bring
// the speccodes into service.
for (int i = 0; i < num_speccodes; ++i)
The above is a simple example of the syntax and steps of specifying a static and a dynamic spiff. We also discussed the steps for generating the speccodes using the spiffs and for loading the speccodes to be utilized at runtime.
The SRE does all of the heavy-lifting in managing the spiffs and speccodes, enabling a clean and straightforward spiff specification mechanism.