プログラマ38の日記

主にプログラムメモです。

Java: 「Apache Velocity」と「StringTemplate」の性能を比べてみる

前回StringTemplateの記事を書きましたが、Apache Velocityと性能を比べてみました。

 

Apache Velocityではまったこと

テンプレートの書き方を忘れていて、テンプレートで変数のプロパティを指定する場合getterが必要でした。。

例えば、Apache Velocityのテンプレートで、${obj.value01}とした場合、プログラム内では、public変数の"public String value01;" では駄目で、メソッドの "public String getValue01() "が必要となります。

(まぁ、Javaコードならgetterは作るとは思いますが、こういう点では「StringTemplate」の柔軟さはいいですね。)

比較結果

出力の時間を比較してみました。

結論としては「Apache Velocity」が早いです。1回目と2回目はちょっと遅いですが、その後の性能が「String Template」の半分の時間で終わっています。扱う件数が1億件とかになってくるとこの処理時間の差で1日ぐらいの差となります。

実行回数 StringTemlate Apache Velocity 差(A-B)
ナノ秒(A) ナノ秒(B)
1 8,810,818 22,477,063 -13,666,245
2 2,216,037 2,628,724 -412,687
3 1,696,282 728,150 968,132
4 1,775,865 653,078 1,122,787
5 1,458,761 719,125 739,636
6 1,586,751 516,474 1,070,277
7 1,512,911 408,584 1,104,327
8 1,441,531 471,759 969,772
9 1,192,115 471,759 720,356
10 1,180,628 430,326 750,302
2から10の平均 1,562,320 780,887 781,434

 

 Apache Velocityのテンプレート

StringTemplateと同様のものをVTLで書きました。「Apache Velocity」のバージョンは1.7を使います。

${callcnt} 回目の出力

変数の差込10個

01 ${value01}
02 ${value02}
03 ${value03}
04 ${value04}
05 ${value05}
06 ${value06}
07 ${value07}
08 ${value08}
09 ${value09}
10 ${value10}

プロパティの差込10個 (1段階)
01 ${obj.value01}
02 ${obj.value02}
03 ${obj.value03}
04 ${obj.value04}
05 ${obj.value05}
06 ${obj.value06}
07 ${obj.value07}
08 ${obj.value08}
09 ${obj.value09}
10 ${obj.value10}

プロパティの差込10個 (2段階)
01 ${obj.prop.value01}
02 ${obj.prop.value02}
03 ${obj.prop.value03}
04 ${obj.prop.value04}
05 ${obj.prop.value05}
06 ${obj.prop.value06}
07 ${obj.prop.value07}
08 ${obj.prop.value08}
09 ${obj.prop.value09}
10 ${obj.prop.value10}

ループ(要素10)
#foreach( $row in $rows ) 
${row.rownum} ${row.value
#end

そして性能を算出したプログラムは次です。

通常「Apache Velocity」はテンプレートファイルから読み込むのですが、「String Template」と同様にテンプレートの文字列から変数を展開するように書いています。

import java.io.File;
import java.io.FileInputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.DecimalFormat;
import java.util.ArrayList;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.RuntimeSingleton;
import org.apache.velocity.runtime.parser.ParseException;
import org.apache.velocity.runtime.parser.node.SimpleNode;

public class VelocitySample {
  
  public static void main(String... args) throws Exception{
    DecimalFormat df = new DecimalFormat("000000");
    
    Template vt = null;
    
    long templatestart = System.nanoTime();
    vt = newTemplate(templateStr());
    long templateend = System.nanoTime();
    System.out.println(templateend - templatestart);

    
    for( int i=1; i<=1000 ; i++) {
      long bindstart = System.nanoTime();    
      new VelocitySample().execute(vt ,i);
      long bindend = System.nanoTime();
      System.out.println( df.format(i) + "回目:" + (bindend - bindstart));
    }    
  }
  
  public static Template newTemplate(String templateString) throws Exception{

/** このように書くことでテンプレートの文字列を内部でコンパイルする **/

    RuntimeServices runtimeServices = RuntimeSingleton.getRuntimeServices();
    StringReader reader = new StringReader(templateString);
    SimpleNode node;
    try {
      node = runtimeServices.parse(reader, "template1");
    } catch (ParseException e) {
      throw new RuntimeException("パースエラー", e);
    }

    Template template = new Template();
    template.setRuntimeServices(runtimeServices);
    template.setData(node);
    template.initDocument();

    return template;
  }
  
  
  public  void execute(Template vt , int callcnt) throws Exception{
    
    
    String rnd = ":" + (int)(Math.random() * 10000);
    
    String value01 = "value01"+rnd;
    String value02 = "value02"+rnd;
    String value03 = "value03"+rnd;
    String value04 = "value04"+rnd;
    String value05 = "value05"+rnd;
    String value06 = "value06"+rnd;
    String value07 = "value07"+rnd;
    String value08 = "value08"+rnd;
    String value09 = "value09"+rnd;
    String value10 = "value10"+rnd;


    Class1 c1 = new Class1();
    c1.value01 = "nest1obj." + value01;
    c1.value02 = "nest1obj." + value02;
    c1.value03 = "nest1obj." + value03;
    c1.value04 = "nest1obj." + value04;
    c1.value05 = "nest1obj." + value05;
    c1.value06 = "nest1obj." + value06;
    c1.value07 = "nest1obj." + value07;
    c1.value08 = "nest1obj." + value08;
    c1.value09 = "nest1obj." + value09;
    c1.value10 = "nest1obj." + value10;


    c1.prop.value01 = "nest21obj." + value01;
    c1.prop.value02 = "nest21obj." + value02;
    c1.prop.value03 = "nest21obj." + value03;
    c1.prop.value04 = "nest21obj." + value04;
    c1.prop.value05 = "nest21obj." + value05;
    c1.prop.value06 = "nest21obj." + value06;
    c1.prop.value07 = "nest21obj." + value07;
    c1.prop.value08 = "nest21obj." + value08;
    c1.prop.value09 = "nest21obj." + value09;
    c1.prop.value10 = "nest21obj." + value10;

    ArrayList<Class2> rows = new ArrayList<>();
    
    for(int i=1; i<=10; i++){
      Class2 c2 = new Class2();
      c2.rownum = i;
      c2.value = "loopvalue" + rnd;
      rows.add(c2);
    }
    
    
    VelocityContext vc = new VelocityContext();
    
    
    
    vc.put("callcnt",callcnt );
    vc.put("value01",value01 );
    vc.put("value02",value02 );
    vc.put("value03",value03 );
    vc.put("value04",value04 );
    vc.put("value05",value05 );
    vc.put("value06",value06 );
    vc.put("value07",value07 );
    vc.put("value08",value08 );
    vc.put("value09",value09 );
    vc.put("value10",value10 );

    vc.put("obj", c1);
    vc.put("rows", rows);
    
    StringWriter writer = new StringWriter();
    vt.merge( vc , writer);
    
    String ret = writer.toString();
    
  }
  
  
  
  
  public  class Class1{
    public String getValue01() {
      return value01;
    }

    public void setValue01(String value01) {
      this.value01 = value01;
    }

    public String getValue02() {
      return value02;
    }

    public void setValue02(String value02) {
      this.value02 = value02;
    }

    public String getValue03() {
      return value03;
    }

    public void setValue03(String value03) {
      this.value03 = value03;
    }

    public String getValue04() {
      return value04;
    }

    public void setValue04(String value04) {
      this.value04 = value04;
    }

    public String getValue05() {
      return value05;
    }

    public void setValue05(String value05) {
      this.value05 = value05;
    }

    public String getValue06() {
      return value06;
    }

    public void setValue06(String value06) {
      this.value06 = value06;
    }

    public String getValue07() {
      return value07;
    }

    public void setValue07(String value07) {
      this.value07 = value07;
    }

    public String getValue08() {
      return value08;
    }

    public void setValue08(String value08) {
      this.value08 = value08;
    }

    public String getValue09() {
      return value09;
    }

    public void setValue09(String value09) {
      this.value09 = value09;
    }

    public String getValue10() {
      return value10;
    }

    public void setValue10(String value10) {
      this.value10 = value10;
    }

    public Nest1 getProp() {
      return prop;
    }

    public void setProp(Nest1 prop) {
      this.prop = prop;
    }

    public String value01;
    public String value02;
    public String value03;
    public String value04;
    public String value05;
    public String value06;
    public String value07;
    public String value08;
    public String value09;
    public String value10;
    
    public Nest1 prop = new Nest1();
  }
  
  public  class Nest1{
    public String getValue01() {
      return value01;
    }
    public void setValue01(String value01) {
      this.value01 = value01;
    }
    public String getValue02() {
      return value02;
    }
    public void setValue02(String value02) {
      this.value02 = value02;
    }
    public String getValue03() {
      return value03;
    }
    public void setValue03(String value03) {
      this.value03 = value03;
    }
    public String getValue04() {
      return value04;
    }
    public void setValue04(String value04) {
      this.value04 = value04;
    }
    public String getValue05() {
      return value05;
    }
    public void setValue05(String value05) {
      this.value05 = value05;
    }
    public String getValue06() {
      return value06;
    }
    public void setValue06(String value06) {
      this.value06 = value06;
    }
    public String getValue07() {
      return value07;
    }
    public void setValue07(String value07) {
      this.value07 = value07;
    }
    public String getValue08() {
      return value08;
    }
    public void setValue08(String value08) {
      this.value08 = value08;
    }
    public String getValue09() {
      return value09;
    }
    public void setValue09(String value09) {
      this.value09 = value09;
    }
    public String getValue10() {
      return value10;
    }
    public void setValue10(String value10) {
      this.value10 = value10;
    }
    public String value01;
    public String value02;
    public String value03;
    public String value04;
    public String value05;
    public String value06;
    public String value07;
    public String value08;
    public String value09;
    public String value10;

  }
  
  public  class Class2 {
    public int getRownum() {
      return rownum;
    }
    public void setRownum(int rownum) {
      this.rownum = rownum;
    }
    public String getValue() {
      return value;
    }
    public void setValue(String value) {
      this.value = value;
    }
    public int rownum;
    public String value;
  }
  
  public static String templateStr() throws Exception{
    File t = new File("template1.txt");
    
    byte[] bytes = new byte[(int)t.length()];
    
    FileInputStream fis = new FileInputStream(t);
    
    fis.read(bytes);
    
    fis.close();
    
    return new String(bytes, "Windows-31J");
  }
}

setter/getterを追加しているのもそうですが、「Apache Velocity」用のコードだけでも多いですね。