The Esper version 8 compiler produces JVM byte code from common EPL expressions such as a+b. For the purpose of computing the sum these are the steps:

  1. Downcast the event underlying object
  2. Obtain the values for “a” and “b” by calling a method on the event underlying object
  3. Perform the addition
  4. Stuff the result in an output event

With regard to step 1, the Esper compiler makes sure to downcast each event object just once regardless of the number of event properties in a given EPL expression.

Relating to step 2, the Esper compiler generates byte code that calls the relevant method. There is no indirection at runtime since the compiled code is specific to the event underlying object.

About step 3, the simplified the byte code tends to give the Java platform just-in-time compiler a chance to do its own optimizations.

As to step 4, depending on the nature of the output event (POJO, object-array etc.) the compiler generates byte code that stuffs the result into the target directly (setter, constructor, index-based array write, or map put depending on the output event underlying object, or other).

Let’s say there is an event that has 100 properties that need to be added up. The sample event for the discussion is:

public static class TestEvent {
    private int v0;
    private int v1;
    private int v2;
    private int v3;
    ... and so on....

    public int getV0() { return v0; }
    public int getV1() { return v1; }
    public int getV2() { return v2; }
    public int getV3() { return v3; }
    ... and so on....
}

Now let’s get to the EPL that adds up the 100 values of each event. It is straight select.

select v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 +
 v12 + v13 + v14 + v15 + v16 + v17 + v18 + v19 + v20 + v21 + v22 + 
 v23 + v24 + v25 + v26 + v27 + v28 + v29 + v30 + v31 + v32 + v33 + 
 v34 + v35 + v36 + v37 + v38 + v39 + v40 + v41 + v42 + v43 + v44 + 
 v45 + v46 + v47 + v48 + v49 + v50 + v51 + v52 + v53 + v54 + v55 + 
 v56 + v57 + v58 + v59 + v60 + v61 + v62 + v63 + v64 + v65 + v66 + 
 v67 + v68 + v69 + v70 + v71 + v72 + v73 + v74 + v75 + v76 + v77 + 
 v78 + v79 + v80 + v81 + v82 + v83 + v84 + v85 + v86 + v87 + v88 + 
 v89 + v90 + v91 + v92 + v93 + v94 + v95 + v96 + v97 + v98 + 
 v99 as c0 from TestEvent

The EPL, above, doesn’t hold any event or other information in memory and therefore the heap size can be tiny if we wanted to. The test setup is JVM 1.8.0_192, heap 256m (-Xms256m –Xmx256m), Intel i7 7700HQ@2.80 GHz.

Voila. Here are the performance test results:

Release              Milliseconds Processing 10 Million Events     
Version 8.1 5422 millis
Version 6.115141 millis

The code for Esper version 8.1 is:

String epl = "@public @buseventtype create schema TestEvent as " + TestEvent.class.getName() + ";\n" +
"select v0+v1+v2+v3+v4+v5+v6+v7+v8+v9+v10+v11+v12+v13+v14+v15+v16+v17+v18+v19+v20+\n" +
"v21+v22+v23+v24+v25+v26+v27+v28+v29+v30+v31+v32+v33+v34+v35+v36+v37+v38+v39+v40+\n" +
"v41+v42+v43+v44+v45+v46+v47+v48+v49+v50+v51+v52+v53+v54+v55+v56+v57+v58+v59+v60+\n" +
"v61+v62+v63+v64+v65+v66+v67+v68+v69+v70+v71+v72+v73+v74+v75+v76+v77+v78+v79+v80+\n" +
"v81+v82+v83+v84+v85+v86+v87+v88+v89+v90+v91+v92+v93+v94+v95+v96+v97+v98+v99 as c0 from TestEvent;\n";
CompilerArguments args = new CompilerArguments();
EPCompiled compiled = EPCompilerProvider.getCompiler().compile(epl, args);

EPRuntime runtime = EPRuntimeProvider.getDefaultRuntime();
EPDeployment deployment = runtime.getDeploymentService().deploy(compiled);

// we add a listener since realistically there would be one
deployment.getStatements()[1].addListener((ne, oe, stmr, r) -> {});

EventSender sender = runtime.getEventService().getEventSender("TestEvent");

// process events
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
    sender.sendEvent(new TestEvent());
}
long delta = System.currentTimeMillis() - start;
System.out.println("Completed in " + delta + " milliseconds");

The code for Esper version 6.1 is:

String epl = "create schema TestEvent as " + TestEvent.class.getName() + ";\n" +
    "@name('out') select v0+v1+v2+v3+v4+v5+v6+v7+v8+v9+v10+v11+v12+v13+v14+v15+v16+v17+v18+v19+v20+\n" +
"v21+v22+v23+v24+v25+v26+v27+v28+v29+v30+v31+v32+v33+v34+v35+v36+v37+v38+v39+v40+\n" +
"v41+v42+v43+v44+v45+v46+v47+v48+v49+v50+v51+v52+v53+v54+v55+v56+v57+v58+v59+v60+\n" +
"v61+v62+v63+v64+v65+v66+v67+v68+v69+v70+v71+v72+v73+v74+v75+v76+v77+v78+v79+v80+\n" +
"v81+v82+v83+v84+v85+v86+v87+v88+v89+v90+v91+v92+v93+v94+v95+v96+v97+v98+v99 as c0 from TestEvent;\n";
EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
epService.getEPAdministrator().getDeploymentAdmin().parseDeploy(epl);

// we add a listener since realistically there would be one
epService.getEPAdministrator().getStatement("out").addListener((ne, oe) -> {});

EventSender sender = epService.getEPRuntime().getEventSender("TestEvent");

// process events
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
    sender.sendEvent(new TestEvent());
}
long delta = System.currentTimeMillis() - start;
System.out.println("Completed in " + delta + " milliseconds");