表达式引擎性能对比

1. 表达式引擎选型

如果要做一个规则引擎,需要匹配一组规则集来做决策,此时就需要一个表达式引擎来辅助。
选择 5 种表达式引擎进行性能对比,从而选择最优表达式引擎和最优方案。Janino、QLExpress、MEVL、JUEL、FEL。
当然,除了计算性能,还要考虑是否满足功能,以及是否有安全漏洞等,本文仅进行表达式引擎的性能对比。

2. 性能测试

2.1 测试维度

  1. 表达式维度:主要采用了3种表达式,表达式的语法在不同的表达式引擎中也不同,详见3.2 。
  • 表达式1: 最常见的场景(多个条件进行and操作):city.equals(‘杭州’) && age<=20
  • 表达式2: 包含特殊的操作(contains):city.equals(‘杭州’) && age<=20 && stringList.contains(str)
  • 表达式3: 一个稍微复杂一点的语法树 :a>1 && ((b>1 || c<1) || (a>1 && b<1 && c>1))
  1. 数据量
  • 1000万
  • 2000万
  • 4000万

2.2 测试方式

根据数据量执行多少次 for 循环,记录下表达式计算的总耗时,单位毫秒 (ms)。为了保证一定的差异性,变量赋值的时候,采用变化的值。代码详见 3.2 。

2.3 结论

  • 表达式1:city.equals(“杭州”) && age<=20
Janino QL Mvel (预编译) Juel Fel
1000万 583 6885 2634 3174 125065
2000万 1171 11363 4167 4318 277257
4000万 1951 22979 8544 9117 519036
  • 表达式2: city.equals(“杭州”) && age<=20 && stringList.contains(str)
Janino QL Mvel (预编译) Juel Fel
1000万 508 6473 2810 2787 166865
2000万 983 12793 4627 5728 339085
4000万 2008 25595 9656 11417 670728
  • 表达式3:a>1 && ((b>1 || c<1) || (a>1 && b<1 && c>1))
Janino QL Mvel (预编译) Juel Fel
1000万 489 3082 2206 3056 167739
2000万 947 6156 3445 5950 321651
4000万 1909 12353 7284 12934 642368

Janino 表达式引擎的性能最优。
处理速度上,Janino > Mvel(预编译) > Juel > QL > Fel 。

3. 附录

3.1 机器配置

本机 Mac 配置:
MacBook Pro (13-inch, 2019, Four Thunderbolt 3 ports)
处理器 2.4 GHz 四核Intel Core i5
内存 16 GB 2133 MHz LPDDR3
图形卡 Intel Iris Plus Graphics 655 1536 MB

3.2 Java 测试代码

  • Janino
import org.codehaus.commons.compiler.CompilerFactoryFactory;
import org.codehaus.commons.compiler.IExpressionEvaluator;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Description:
 *
 * @author mwt
 * @version 1.0
 * @date 2020/5/21
 */
public class JaninoExpressionTest {
   

    @Test
    public void test1() throws Exception {
   
        String ruleExpress = "city.equals(\"杭州\") && age<=20";

        long count1 = 1000 * 10000;
        System.out.println("\nJaninoExpression1 数据量 1000 万:");
        execute(count1, ruleExpress);

        long count2 = 2000 * 10000;
        System.out.println("\nJaninoExpression1 数据量 2000 万:");
        execute(count2, ruleExpress);

        long count3 = 4000 * 10000;
        System.out.println("\nJaninoExpression1 数据量 4000 万:");
        execute(count3, ruleExpress);
    }


    @Test
    public void test2() throws Exception {
   
        String ruleExpress = "city.equals(\"杭州\") && age<=20 && stringList.contains(str)";

        long count1 = 1000 * 10000;
        System.out.println("\nJaninoExpression2 数据量 1000 万:");
        execute(count1, ruleExpress);

        long count2 = 2000 * 10000;
        System.out.println("\nJaninoExpression2 数据量 2000 万:");
        execute(count2, ruleExpress);

        long count3 = 4000 * 10000;
        System.out.println("\nJaninoExpression2 数据量 4000 万:");
        execute(count3, ruleExpress);

    }

    @Test
    public void test3() throws Exception {
   
        String ruleExpress = "a>1 && ((b>1 || c<1) || (a>1 && b<1 && c>1))";

        long count1 = 1000 * 10000;
        System.out.println("\nJaninoExpression3 数据量 1000 万:");
        execute(count1, ruleExpress);

        long count2 = 2000 * 10000;
        System.out.println("\nJaninoExpression3 数据量 2000 万:");
        execute(count2, ruleExpress);

        long count3 = 4000 * 10000;
        System.out.println("\nJaninoExpression3 数据量 4000 万:");
        execute(count3, ruleExpress);
    }

    private void execute(long count, String expression) throws Exception {
   
        IExpressionEvaluator expressionEvaluator =
                CompilerFactoryFactory.getDefaultCompilerFactory().newExpressionEvaluator();
        expressionEvaluator.setExpressionType(boolean.class);
        expressionEvaluator.setParameters(
                new String[]{
   "city", "age", "stringList", "str", "a", "b", "c"},
                new Class[]{
   String.class, Integer.class, List.class, String.class,
                        Integer.class, Integer.class, Integer.class}
        );
        expressionEvaluator.cook(expression);

        long start = System.currentTimeMillis();
        List<String> stringList = new ArrayList<String>(2);
        stringList.add("hello");
        stringList.add("world");
        Random random = new Random();
        for (int i = 0; i < count; i++) {
   
            Object[] arguments = new Object[7];
            int age = random.nextInt(50);
            if (i % 2 == 0) {
   
                arguments[0] = "杭州";
            } else {
   
                arguments[0] = "北京";

            }
            arguments[1] = age;
            arguments[2] = stringList;
            if (i % 3 == 0) {
   
                arguments[3] = "hello";
            } else if (i % 3 == 1) {
   
                arguments[3] = "world";
            } else {
   
                arguments[3] = "anything";
            }
            arguments[4] = random.nextInt(2);
            arguments[5] = random.nextInt(2);
            arguments[6] = random.nextInt(2);

            Object res = expressionEvaluator.evaluate(arguments);
        }
        long end = System.currentTimeMillis();
        long intervalInMs = end - start;
        float avg = (float) count / intervalInMs * 1000;

        System.out.println("总耗时毫秒:" + intervalInMs);
        System.out.println("每秒处理条数:" + avg);
    }
}
  • QL
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Description:
 *
 * @author mwt
 * @version 1.0
 * @date 2020/5/21
 */
public class QLExpressTest {
   

    @Test
    public void test1() throws Exception {
   
        String ruleExpress = "city.equals(\"杭州\") && age<=20";

        long count1 = 1000 * 10000;
        System.out.println("\nQLExpress1 数据量 1000 万:");
        execute(count1, ruleExpress);

        long count2 = 2000 * 10000;
        System.out.println("\nQLExpress1 数据量 2000 万:");
        execute(count2, ruleExpress);

        long count3 = 4000 * 10000;
        System.out.println("\nQLExpress1 数据量 4000 万:");
        execute(count3, ruleExpress);
    }


    @Test
    public void test2() throws Exception {
   
        String ruleExpress = "city.equals(\"杭州\") && age<=20 && stringList.contains(str)";

        long count1 = 1000 * 10000;
        System.out.println(
  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值