IncQuery MoDisco Benchmark

Introduction

The benchmark presented on this page is provided by the developers of the Epsilon tool and it applies model queries to find class properties in existing Java classes. A class property is a variable with name X that has two corresponding methods in the same class, with name getX/setX or isX/setX.

class C{
 int X;
 
 int getX(){return X;}
 
 void isX(int newX){X = newX;}
}

The input models for the benchmark were created by the MoDisco tool from existing components of the Epsilon tool and the Eclipse framework. The Instance models used for the benchmark are publicly available.

Benchmark setup

The following pattern definitions were provided by the benchmark authors:

pattern nameOfElement(Element, Name) = {
 NamedElement(Element);
 NamedElement.name(EAttribute, Element, Name);
 EString(Name);
}
	
pattern classes(Element) = {
 ClassDeclaration(Element);
}	
	
pattern fieldDeclaration(Element) = {
 FieldDeclaration(Element);
}
	
pattern fieldAndFrag(A, B) = {
 FieldDeclaration(A);
 VariableDeclarationFragment(B);
 AbstractVariablesContainer.fragments(EReference, A, B);
}
	
pattern classWithBody(A, B) = {
 ClassDeclaration(A);
 BodyDeclaration(B);
 AbstractTypeDeclaration.bodyDeclarations(EReference, A, B);
}
	
pattern classWithVariables(Class, Variable) = {
 ClassDeclaration(Class);
 find classWithBody(Class, Body);
 find fieldAndFrag(Body, Variable);
}

pattern classWithMethods(Class, Method) = {
 ClassDeclaration(Class);
 MethodDeclaration(Method);
 AbstractTypeDeclaration.bodyDeclarations(EReference, Class, Method);
}

pattern matchedClass(Class, Variable) = {
 ClassDeclaration(Class);
 find classWithVariables(Class, Variable);
 find nameOfElement(Variable, VarName);
 find classWithMethods(Class, Method1);
 find classWithMethods(Class, Method2);
 find nameOfElement(Method1, Method1Name);
 find nameOfElement(Method2, Method2Name);
 check((toLower(value(Method1Name)) == "get" + toLower(value(VarName))) || 
  (toLower(value(Method1Name)) == "is" + toLower(value(VarName))));
 check(toLower(value(Method2Name)) == "set" + toLower(value(VarName)));
}

Results

We set up the benchmark using the provided java.ecore and the four EMF models with both EMF-IncQuery 0.4 and the current version of VIATRA2, based on the guidelines written in the Performance page.

EMF-IncQuery 0.4

The following table lists for each input model the size of the original model, the total memory footprint (see Performance). We have used several VM argument configurations ranging from a high of 2,5GB memory to the lowest of 700MB memory. For each configuration, we listed the time it takes for EMF to load the XMI file and the initialization time for EMF-IncQuery. Note that the collection of matches from the Rete network is not listed, but it was around 10-30 ms in each case.

EMF-IncQuery 0.4 Xmx2586, PermGen=1500 Xmx1000, PermGen=512 Xmx700, PermGen=256
Model name Model size [MB] Used Memory [MB] EMF load [ms] IncQuery init [ms] EMF load [ms] IncQuery init [ms] EMF load [ms] IncQuery init [ms]
Epsilon.commons 0,3 56 314 217 337 224 403 230
Epsilon.eol.engine 21 85 1624 701 1647 932 1596 769
Eclipse.ui.ide 85 185 4051 895 3927 1508 3760 1072
Eclipse.ui.workbench 165 343 6319 2669 7457 3880 6768 3185

Note, that we also measured the optimized code (by Gábor Bergmann) from the VIATRA2 solution to see how it affects performance.

EMF-IncQuery 0.4 Xmx700, PermGen=256
Model name Model size [MB] Used Memory [MB] EMF load [ms] IncQuery init [ms]
Epsilon.commons 0,3 57 393 203
Epsilon.eol.engine 21 109 1877 1193
Eclipse.ui.ide 85 184 4051 1764
Eclipse.ui.workbench 165 339 6915 3060

VIATRA2

We measured the pattern matching off VIATRA2 by adding the @incremental annotation to the machine, so that it uses the Incremental Pattern matcher. We also removed the println() calls from the code, since writing to the output takes considerable time that should not be measured. For the results, we used the built-in time measurement of VIATRA that outputs the time of imports and transfromations on the system output.

rule main() = let Buf = getBuffer("core://out") in seq{
 forall C, A with find matchedClass(C, A) do
  println(Buf,name(C) + "." + name(A));
}

The following VM arguments were used for these measurements: -Xms200m -Xmx6000m -XX:MaxPermSize=3536M -XX:+UseParallelGC -XX:PermSize=1528M. Note that we have been able to load the largest model with these settings but we were unable to run the transformation.

Model name VIATRA import [ms] original [ms]
Epsilon.commons 943 251
Epsilon.eol.engine 13892 102103
Eclipse.ui.ide 65009 21638
Eclipse.ui.workbench 206297 -

Optimized VIATRA2

Next, we went on to modify the pattern definitions themselves to create the optimized, but equivalent versions based on our experience with the Rete network and the incremental pattern matcher.

pattern fieldAndFrag(A, B) = {
 FieldDeclaration(A);
 VariableDeclarationFragment(B);
 AbstractVariablesContainer.fragments(EReference, A, B);
}

pattern classWithBody(A, B) = {
 ClassDeclaration(A);
 BodyDeclaration(B);
 AbstractTypeDeclaration.bodyDeclarations(EReference, A, B);
}

pattern classWithVariableName(Class, VariableName) = {
 ClassDeclaration(Class);
 find classWithBody(Class, Body);
 find fieldAndFrag(Body, Variable);
 NamedElement.name(EAttribute, Variable, VariableName);
 EString(VariableName);
}

pattern classWithMethodName(Class, MethodName) = {
 ClassDeclaration(Class);
 MethodDeclaration(Method);
 AbstractTypeDeclaration.bodyDeclarations(EReference, Class, Method);
 NamedElement.name(EAttribute, Method, MethodName);
 EString(MethodName);
}

pattern matchedClass(Class, VarName) = {
 ClassDeclaration(Class);
 find classWithVariableName(Class, VarName);
 find classWithMethodName(Class, Method1Name);
 find classWithMethodName(Class, Method2Name);
 check(str.equalsIgnoreCase(value(Method1Name), "get" + value(VarName)) ||
  str.equalsIgnoreCase(value(Method1Name), "is" + value(VarName)));
 check(str.equalsIgnoreCase(value(Method2Name), "set" + value(VarName)));
}

Finally, we asked our fellow researcher Gábor Bergmann, the original developer of the incremental pattern matcher (and its underlying Rete implementation) to see if he can do a bit more optimization. These results are given in the last column. Note that the most noticable performance gain is from the decomposition of the main pattern to the two subpatterns for finding getters and setters and the main pattern connects these patterns.

pattern nameOfElement(Element, Name) = 
{
 NamedElement(Element);
 NamedElement.name(EAttribute, Element, Name);
 EString(Name);
}

pattern fieldAndFrag(A, B) =
{
 FieldDeclaration(A);
 VariableDeclarationFragment(B);
 AbstractVariablesContainer.fragments(EReference, A, B);
}

pattern classWithBody(A, B) =
{
 ClassDeclaration(A);
 BodyDeclaration(B);
 AbstractTypeDeclaration.bodyDeclarations(EReference, A, B);
}

pattern classWithVariables(Class, Variable) =
{
 ClassDeclaration(Class);
 find classWithBody(Class, Body);
 find fieldAndFrag(Body, Variable);
}

pattern classWithVarName(Class, Variable, VarName) =
{
 find classWithVariables(Class, Variable);
 find nameOfElement(Variable, VarName);
}

pattern classWithMethods(Class, Method) =
{
 ClassDeclaration(Class);
 MethodDeclaration(Method);
 AbstractTypeDeclaration.bodyDeclarations(EReference, Class, Method);
}

pattern classWithMethName(Class, Method, MethodName) =
{
 find classWithMethods(Class, Method);
 find nameOfElement(Method, MethodName);
}

pattern hasGetter(Class, Variable) = {
 find classWithVarName(Class, Variable, VarName);
 find classWithMethName(Class, Method, MethodName);
 check(str.equalsIgnoreCase(value(MethodName), "get" + value(VarName)) ||
  str.equalsIgnoreCase(value(MethodName), "is" + value(VarName)));		
}
pattern hasSetter(Class, Variable) = {
 find classWithVarName(Class, Variable, VarName);
 find classWithMethName(Class, Method, MethodName);
 check(str.equalsIgnoreCase(value(MethodName), "set" + value(VarName)));
}

pattern matchedClass(Class, Variable) =
{
 find hasGetter(Class, Variable);
 find hasSetter(Class, Variable);
}

The following VM arguments were used for all measurements Xmx6000,PermGen=3568.

Model name import original [ms] optimized  [ms] Gabor [ms]
Epsilon.commons 943 251 126 79
Epsilon.eol.engine 13892 102103 21201 1030
Eclipse.ui.ide 65009 21638 16491 2122
Eclipse.ui.workbench 206297 - - 9142

Note that using the pattern definitions created by Gábor, we were able to finally run the transformation for the largest model as well!

EMF-IncQuery 0.6

The new generation of EMF-IncQuery uses a new Xtext-based query definition editor and provides a nice Query Explorer for testing queries dynamically at the same time as they are defined, to remove the development loop of code generation and using a runtime Eclipse.

package modisco

import "http://www.eclipse.org/MoDisco/Java/0.2.incubation/java"
import "http://www.eclipse.org/emf/2002/Ecore"

pattern fieldAndFrag(A, B)= {
 FieldDeclaration(A);
 VariableDeclarationFragment(B);
 AbstractVariablesContainer.fragments(A, B);
}

pattern classWithBody(A, B)= {
 ClassDeclaration(A);
 BodyDeclaration(B);
 AbstractTypeDeclaration.bodyDeclarations(A, B);
}

pattern classWithVariableName(Clss, VariableName)= {
 ClassDeclaration(Clss);
 find classWithBody(Clss, Body);
 find fieldAndFrag(Body, Variable);
 NamedElement.name(Variable, VariableName);
 EString(VariableName);
}

pattern classWithMethodName(Clss, MethodName)= {
 ClassDeclaration(Clss);
 MethodDeclaration(Method);
 AbstractTypeDeclaration.bodyDeclarations(Clss, Method);
 NamedElement.name(Method, MethodName);
 EString(MethodName);
}

pattern matchedClass(Clss, VarName)= {
 ClassDeclaration(Clss);
 find classWithVariableName(Clss, VarName);
 find classWithMethodName(Clss, Method1Name);
 find classWithMethodName(Clss, Method2Name);
 check(Method1Name.toString.equalsIgnoreCase("get".concat(VarName.toString)) ||
   Method1Name.toString.equalsIgnoreCase("is".concat(VarName.toString)));
 check(Method2Name.toString.equalsIgnoreCase("set".concat(VarName.toString)));
}

In the current version, the above definition can only be checked in the Query Explorer due to a minor bug caused by Xtext and Xbase type providers clashing. The results of the measurements with the same settings as for EMF-IncQuery 0.4 will be posted soon!

EMF-IncQuery New Generation Xmx2586, PermGen=1500 Xmx1000, PermGen=512 Xmx700, PermGen=256
Model name Model size [MB] Used Memory [MB] EMF load [ms] IncQuery init [ms] EMF load [ms] IncQuery init [ms] EMF load [ms] IncQuery init [ms]
Epsilon.commons 0,3              
Epsilon.eol.engine 21              
Eclipse.ui.ide 85              
Eclipse.ui.workbench 165