高性能EL——Fel探秘,兼谈EL
Fel是最近javaeye比较火的关键词,这是由网友lotusyu
开发的一个高性能的EL,从作者给出的数据来看,性能非常优异,跟前段时间温少开源的Simple EL
有的一拼。首先要说,这是个好现象,国内的开源项目越来越多,可以看出开发者的水平是越来越高了,比如我最近还看到有人开源的类似kestel的轻量级MQ——fqueue
也非常不错,有兴趣可以看下我的分析《fqueue初步分析
》。
进入正文,本文是尝试分析下Fel的实现原理,以及优缺点和aviator
——我自己开源的EL之间的简单比较。
Fel的实现原理跟Simple
EL是类似,都是使用template生成中间代码——也就是普通的java代码,然后利用javac编译成class,最后运行,当然,这个过程都是动
态的。JDK6已经引入了编译API,在此之前的版本可以调用sun的类来编译,因为javac其实就是用java实现的。回到Fel里
面,FelCompiler15就是用 com.sun.tools.javac.Main
来编译,而FelCompiler16用标准的javax.tools.JavaCompiler
来编译的。
文法和语法解释这块是使用antlr这个parse generator生成的,这块不多说,有兴趣可以看下antlr,整体一个运行的过程是这样:
expression string
->
antlr
->
AST
->
comiple
->
java source template
->
java
class
->
Expression
这个思路我在实现aviator
之前
就想过,但是后来考虑到API需要用的sun独有的类,而且要求classpath必须有tools.jar这个依赖包,就放弃了这个思路,还是采用
ASM生成字节码的方式。题外,velocity的优化可以采用这个思路,我们有这么一个项目是这么做的,也准备开源了。
看看Fel生成的中间代码,例如a+b这样的一个简单的表达式,假设我一开始不知道a和b的类型,编译是这样:
FelEngine fel
=
new
FelEngineImpl();
Expression exp
=
fel.compile(
"
a+b
"
,
null
);
我稍微改了下FEL的源码,让它打印中间生成的java代码,a+b生成的中间结果为:
package
com.greenpineyu.fel.compile;
import
com.greenpineyu.fel.common.NumberUtil;
import
com.greenpineyu.fel.Expression;
import
com.greenpineyu.fel.context.FelContext;
import
org.apache.commons.lang.ObjectUtils;
import
org.apache.commons.lang.StringUtils;
public
class
Fel_0
implements
Expression{
public
Object eval(FelContext context) {
java.lang.Object var_1
=
(java.lang.Object)context.get(
"
b
"
);
//
b
java.lang.Object var_0
=
(java.lang.Object)context.get(
"
a
"
);
//
a
return
(ObjectUtils.toString(var_0))
+
(ObjectUtils.toString(var_1));
}
}
可见,FEL对表达式解析和解释后,利用template生成这么一个普通的java类,而a和b都从context中获取并转化为Object
类型,这里没有做任何判断就直接认为a和b是要做字符串相加,然后拼接字符串并返回。
问题出来了,因为没有在编译的时候传入context(我们这里是null),FEL会将a和b的类型默认都为java.lang.Object,a+b解释为字符串拼接。但是运行的时候,我完全可以传入a和b都为数字,那么结果就非常诡异了:
FelEngine fel
=
new
FelEngineImpl();
Expression exp
=
fel.compile(
"
a+b
"
,
null
);
Map
<
String, Object
>
env
=
new
HashMap
<
String, Object
>
();
env.put(
"
a
"
,
1
);
env.put(
"
b
"
,
3.14
);
System.out.println(exp.eval(
new
MapContext(env)));
输出:
13.14
1+3.14的结果,作为字符串拼接就是13.14,而不是我们想要的4.14。如果将表达式换成a*b,就完全运行不了
com.greenpineyu.fel.exception.CompileException:
package
com.greenpineyu.fel.compile;
import
com.greenpineyu.fel.common.NumberUtil;
import
com.greenpineyu.fel.Expression;
import
com.greenpineyu.fel.context.FelContext;
import
org.apache.commons.lang.ObjectUtils;
import
org.apache.commons.lang.StringUtils;
public
class
Fel_0
implements
Expression{
public
Object eval(FelContext context) {
java.lang.Object var_1
=
(java.lang.Object)context.get(
"
b
"
);
//
b
java.lang.Object var_0
=
(java.lang.Object)context.get(
"
a
"
);
//
a
return
(var_0)
*
(var_1);
}
}
[Fel_0.java:
14
: 运算符
*
不能应用于 java.lang.Object,java.lang.Object]
at com.greenpineyu.fel.compile.FelCompiler16.compileToClass(FelCompiler16.java:
113
)
at com.greenpineyu.fel.compile.FelCompiler16.compile(FelCompiler16.java:
87
)
at com.greenpineyu.fel.compile.CompileService.compile(CompileService.java:
66
)
at com.greenpineyu.fel.FelEngineImpl.compile(FelEngineImpl.java:
62
)
at TEst.main(TEst.java:
14
)
Exception in thread
"
main
"
java.lang.NullPointerException
at TEst.main(TEst.java:
18
)
这个问题对于Simple EL同样存在,如果没有在编译的时候能确定变量类型,这无法生成正确的中间代码,导致运行时出错,并且有可能造成非常诡异的bug。
这个问题的本质是因为Fel和Simple EL没有自己的类型系统,他们都是直接使用java的类型的系统,并且必须在编译的时候确定变量类型,才能生成高效和正确的代码,我们可以将它们称为“强类型的EL“。
现在让我们在编译的时候给a和b加上类型,看看生成的中间代码:
FelEngine fel
=
new
FelEngineImpl();
fel.getContext().set(
"
a
"
,
1
);
fel.getContext().set(
"
b
"
,
3.14
);
Expression exp
=
fel.compile(
"
a+b
"
,
null
);
Map
<
String, Object
>
env
=
new
HashMap
<
String, Object
>
();
env.put(
"
a
"
,
1
);
env.put(
"
b
"
,
3.14
);
System.out.println(exp.eval(
new
MapContext(env)));
查看中间代码:
package
com.greenpineyu.fel.compile;
import
com.greenpineyu.fel.common.NumberUtil;
import
com.greenpineyu.fel.Expression;
import
com.greenpineyu.fel.context.FelContext;
import
org.apache.commons.lang.ObjectUtils;
import
org.apache.commons.lang.StringUtils;
public
class
Fel_0
implements
Expression{
public
Object eval(FelContext context) {
double
var_1
=
((java.lang.Number)context.get(
"
b
"
)).doubleValue();
//
b
double
var_0
=
((java.lang.Number)context.get(
"
a
"
)).doubleValue();
//
a
return
(var_0)
+
(var_1);
}
}
可以看到这次将a和b都强制转为double类型了,做数值相加,结果也正确了:
4.140000000000001
Simple EL我没看过代码,这里猜测它的实现也应该是类似的,也应该有同样的问题。
相比来说,aviator
这是一个弱类型的EL
,在编译的时候不对变量类型做任何假设,而是在运行时做类型判断和自动转化
。过去提过,我给aviator的定位是一个介于EL和script之间的东西,它有自己的类型系统
。
例如,3这个数字,在java里可能是long,int,short,byte,而aviator统一为AviatorLong这个类型。为了在这两个类
型之间做适配,就需要做很多的判断和box,unbox操作。这些判断和转化都是运行时进行的,因此aviator没有办法做到Fel这样的高效,但是已
经做到至少跟groovy这样的弱类型脚本语言一个级别,也超过了JXEL这样的纯解释EL,具体可以看这个性能测试
。
强类型还是弱类型,这是一个选择问题,如果你能在运行前就确定变量的类型,那么使用Fel应该可以达到或者接近于原生java执行的效率,但是失去了灵活性;如果你无法确定变量类型,则只能采用弱类型的EL。
EL涌现的越来越多,这个现象有点类似消息中间件领域,越来越多面向特定领域的轻量级MQ的出现,而不是原来那种大而笨重的通用MQ大行其道,一方面是互
联网应用的发展,需求不是通用系统能够满足的,另一方面我认为也是开发者素质的提高,大家都能造适合自己的轮子。从EL这方面来说,我也认为会有越来越多
特定于领域的,优点和缺点一样鲜明的EL出现,它们包含设计者自己的目标和口味,选择很多,就看取舍。
分享到:
相关推荐
Fel支持两种执行模式:解释执行和编译执行,用户可以根据具体的性能要求选择合适的执行方式。 在解释执行模式下,表达式被编译成字节码,并且整个编译过程是可扩展和可替换的。Fel引擎的编译执行实际上是指将表达式...
Fel引擎,全称为Fast Expression Language,是一个轻量级、高性能的表达式计算引擎。在0.9版本中,它提供了强大的动态计算功能,使得开发者能够在运行时评估复杂的逻辑和数学表达式。这个版本的发布,旨在提升应用...
Fel在源自于企业项目,设计目标是为了满足不断变化的功能需求和性能需求。 Fel是开放的,引擎执行中的多个模块都可以扩展或替换。Fel的执行主要是通过函数实现,运算符(+、-等都是Fel函数),所有这些函数都是可以...
Fel(Fast Expression Language)是一种高性能的表达式引擎,旨在满足企业级应用对于表达式处理的需求。它具备快速执行的能力,与ognl等其他流行表达式引擎相比,具有更高的性能。 #### 二、核心概念 - **EL ...
Fel(Fast Expression Language)是一种轻量级、高性能的表达式语言,主要用于在应用程序中进行动态计算和数据操作。它提供了一种简洁的语法,使得开发者可以方便地在运行时评估表达式,从而实现灵活的数据处理和...
它被设计用来在程序中动态地执行计算任务,尤其是在处理规则引擎、工作流控制或数据过滤等场景时,能够提供灵活且高性能的解决方案。 在描述中提到,下载fel-0.8.zip后,只需将其解压并将fel-0.8.jar文件放入项目中...
5. **性能优势**:由于Fast-el的编译机制,它能比其他基于字符串解析的表达式语言更快地执行,提高了整体应用的性能。 6. **学习曲线**:Fast-el的语法简洁且直观,对Java开发者来说易于学习和理解,减少了学习新...
fel轻量高效的表达式计算引擎jar包
fel计算引擎0.9官方版本,0.9的版本多了很多方法,支持了大数高精度,maven没有找到直接pom依赖,下载到本地再依赖本地jar包来进行操作
我自己移植的windows下的sunxitools-fel,可以用于全志cpu的fel模式。cpu调试
Fel是轻量级的高效的表达式计算引擎
若usb设备不能正常识别,可以用zadig软件给usb设备安装驱动。在烧录荔枝派flash时,往往需要进入fel模式,将fel-sdboot文件写入sd卡能够使荔枝派上电直接进入fel模式,从而使系统检测出usb fel设备。
Fel在源自于企业项目,设计目标是为了满足不断变化的功能需求和性能需求。 Fel是开放的,引擎执行中的多个模块都可以扩展或替换。Fel的执行主要是通过函数实现,运算符(+、-等都是Fel函数),所有这些函数都是可以...
fel-0.8.jar...........................................................................
Fel表达式引擎是一种高性能的表达式引擎,支持编译执行和解释执行两种模式。它可以处理海量数据,执行速度基本与Java字节码执行速度一样快。 1. FelEngine的使用 FelEngine是Fel表达式引擎的核心组件。用户可以...
全志soc 使用usb进行nand flash 刷机的uart log
【标题】"bpi-fel-mass-storage-gui4win v1.002.zip" 是一个用于全志(Allwinner)处理器平台的固件升级工具的压缩包,版本号为1.002。该工具主要是为了方便用户通过图形化界面进行固件的烧录和管理。 【描述】描述...
《Genesis 1.3:与时间相关的FEL代码——开源技术深度解析》 在现代物理学领域,自由电子激光(Free-Electron Laser, FEL)是一种先进的光源技术,它利用高能电子束与电磁场相互作用产生高亮度、短脉冲的相干光。 ...