Speed Execution with the Compilable Interface
A script engine typically evaluates a script via an interpreter, which consists of a front-end parser for parsing the script into intermediate code, and a back-end executor for executing this code. Because parsing the script into intermediate code must be performed each time the script is evaluated, there is an impact on performance.
Performance can be improved by having the script engine store the intermediate code for execution at a later time. To make this possible, the Scripting API provides a Compilable interface and a CompiledScript class. Compilable provides a pair of compile() methods that parse the script and generate the intermediate code, and return instances of CompiledScript subclasses that store this code. CompiledScript provides three eval() methods for executing the intermediate code. These methods do not parse the script.
Script engines that support this compilation feature implement the Compilable interface. Because not every engine will support compilation, it is important to test for this support before casting the engine to a Compilable, as follows:
ScriptEngine engine = … Compilable compilable = null; if (engine instanceof Compilable) compilable = (Compilable) engine;
To demonstrate the benefits of compilation, I've prepared an application that repeatedly evaluates a script through parsing and executing the intermediate code (for a specific number of iterations), compiles the script, and repeatedly evaluates the script by executing the intermediate code only (for the same number of iterations). Each loop is timed and the resulting time is output. Listing 6 presents the application's source code.
Listing 6: ScriptDemo6.java
// ScriptDemo6.java import java.io.*; import javax.script.*; public class ScriptDemo6 { final static int ITER_MAX = 50000; public static void main (String [] args) throws ScriptException { // Create a ScriptEngineManager that discovers all script engine // factories (and their associated script engines) that are visible to // the current thread's classloader. ScriptEngineManager manager = new ScriptEngineManager (); // Obtain a ScriptEngine that supports the JavaScript short name. ScriptEngine engine = manager.getEngineByName ("JavaScript"); // Specify a simple script to demonstrate compilation improvement. String script = "function sum (x)"+ "{"+ " return x*(x+1)/2;"+ "};"; // Time script parsing and intermediate code execution. long now = System.currentTimeMillis (); for (int i = 0; i < ITER_MAX; i++) engine.eval (script); System.out.println (System.currentTimeMillis () - now); Compilable compilable = null; if (engine instanceof Compilable) { compilable = (Compilable) engine; CompiledScript cs = compilable.compile (script); // Time intermediate code execution. now = System.currentTimeMillis (); for (int i = 0; i < ITER_MAX; i++) cs.eval (); System.out.println (System.currentTimeMillis () - now); } } }
One execution of ScriptDemo6 created the following output, which shows a boost in performance due to compilation:
5234 3828