参评的几款模板引擎为:
XMLTemplate(简称XT)
Velocity(简称VT)
CommonTemplate(简称CT)
FreeMarker(简称FT)
Smarty4j(简称ST)
直接的java代码
以下所有测评的结果单位都是ms
性能评测考虑以下几个方面:变量输出/循环/分支,这三大类调用构成了普通模板80%以上的功能。
测试方法为双层循环,输出的中间体是一个空的不执行任何操作的Writer类,
尽可能的减少模板外的性能影响因素,基本的逻辑伪代码描述如下:
for (int i = 0; i < outerTime; i++) {
for (int j = 0; j < innerTime; j++) {
testXMLTemplate();
}
for (int j = 0; j < innerTime; j++) {
testVelocityTemplate();
}
for (int j = 0; j < innerTime; j++) {
testCommonTemplate();
}
for (int j = 0; j < innerTime; j++) {
testFreeMarker();
}
for (int j = 0; j < innerTime; j++) {
testSmarty4j();
}
for (int j = 0; j < innerTime; j++) {
testJavaCode();
}
}
第一步,测试循环输出ascii码表,各模板引擎文件为
XT:asciitable.xhtml
${name}
${cell} | |
---|---|
${row} | ${row}${cell}; |
VT:asciitable.vm
${name}
#foreach($cell in $data)
${cell}#end
#foreach($row in $data)
${row}#foreach($cell in $data )
${row}${cell};#end
#end
CT:asciitable.ct
${name}
$for{cell:data}
${cell}$end
$for{row:data}
${row}$for{cell:data}
${row}${cell};$end
$end
FT:asciitable.ftl
${name}
${cell} |
---|
#list>
${row}${row}${cell};#list>
#list>
ST:asciitable.html
{$name}
{section loop=$data name="cell"}
{$cell}{/section}
{section loop=$data name="row"}
{$row}{section loop=$data name="cell"}
{$row}{$cell};{/section}
{/section}
JAVA:asciitable.java
package org.jside.tt;
import java.io.Writer;
import java.util.List;
import java.util.Map;
public class asciitable implements ICode {
@Override
public void execute(Map context, Writer writer) throws Exception {
List data = (List) context.get("data");
String name = (String) context.get("name");
String border = (String) context.get("border");
writer.write("
");
writer.write(name);
writer.write("/n
writer.write(border);
writer.write("/">/n/t
/n/t/t /n");for (String cell : data) {
writer.write("/t/t
");writer.write(cell);
writer.write("
/n");}
writer.write("/t
/n");for (String row : data) {
writer.write("/t
/n");writer.write(row);
writer.write("
/n");for (String cell : data) {
writer.write("/t/t
");writer.write(row);
writer.write(cell);
writer.write("
/n");}
writer.write("/t
/n");}
writer.write("
/n}
}
在outerTime=100与innerTime=100时,共循环10000次,平均的结果约是:
=============runing time===============
xt:2149
vt:3499
ct:72254
ft:2761
st:1235
CODE:1321
第二步,在最内层的循环中多输出一个对象,测试新增输出时的性能影响,最内层的那一行改造如下:
XT:
${name}:${row}${cell};VT:
${name}:${row}${cell};CT:
${name}:${row}${cell};FT:
${name}:${row}${cell};ST:
{$name}:{$row}{$cell};JAVA:
writer.write("/t/t
");writer.write(name);
writer.write(":");
writer.write(row);
writer.write(cell);
writer.write("
/n");在outerTime=100与innerTime=100时,共循环10000次,平均的结果约是:
=============runing time===============
xt:3549
vt:4748
ct:103453
ft:4251
st:1750
CODE:1811
第三步,测试分支判断对整体性能的影响,在最内层的循环中输出前加一个分支控制,使它仅输出A-Z对应的ASCII码表,改造如下:
XT:
${row}${cell};nbsp;VT:
#if(($row=="4" && $cell!="0") || ($row=="5" && $cell!="B" && $cell!="C" && $cell!="D" && $cell!="E" && $cell!="F"))${row}${cell};#else #endCT:
$if{(row=="4" && cell!="0") || (row=="5" && cellFT:
${row}${cell}; #if>ST:
{if ($row==='4' && $cell!=='0') || ($row==='5' && $cellJAVA:
writer.write("/t/t
");if ((row.equals("4") && !cell.equals("0"))
|| (row.equals("5") && cell.compareTo("B") < 0)) {
writer.write("");
writer.write(row);
writer.write(cell);
} else {
writer.write(" ");
}
writer.write("
/n");考虑到比较的问题,也可以对整个循环进行优化
for (String row : data) {
char cRow = row.charAt(0);
writer.write("/t
/n");writer.write(row);
writer.write("
/n");for (String cell : data) {
char cCell = cell.charAt(0);
writer.write("/t/t
");if ((cRow == '4' && cCell != '0') || (cRow == '5' && cCell < 'B')) {
writer.write("");
writer.write(row);
writer.write(cell);
} else {
writer.write(" ");
}
writer.write("
/n");}
writer.write("/t
/n");}
在outerTime=100与innerTime=100时,共循环10000次,平均的结果约是:
=============runing time===============
xt:3498
vt:2422
ct:153280
ft:7124
st:1142
CODE:1027(优化后940)
结论:
ST在三种常见的模板操作中的表现均极其优秀,除了条件处理效率略低于直接书写的JAVA代码,其它情况下与直接书写JAVA代码效率完全一致,而且在三种操作中,总的执行开销差异非常小。
XT在分支的处理中考虑与JS兼容带来了额外开销,但总体性能仍然占优,只是如果需要过多的XML转义可能影响阅读
FT在分支测试中表现差的原因应该是写法不是最优的,总体来说,性能与VT不相上下
CT的表现最差,在各项操作中均比其它的引擎慢了1-2个数量级