Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

性能变差了 #96

Closed
0x2c0d opened this issue Jan 3, 2019 · 16 comments
Closed

性能变差了 #96

0x2c0d opened this issue Jan 3, 2019 · 16 comments

Comments

@0x2c0d
Copy link

0x2c0d commented Jan 3, 2019

    //4.1 解释执行  68秒 100w次
    //4.1 预编译  1274ms  1000w次
    //3.2 预编译 672ms  1000w次
    public static void expParserTest4() {

    	  String expression = "a+bbb>cccc";
     	   Map<String, Object> env = new HashMap<String, Object>(3);
     	   env.put("a",new Integer(111));
     	   env.put("bbb",new Integer(222));
     	   env.put("cccc",new Integer(3432));

    	  long st = System.currentTimeMillis();
    	   int i = 0;

         Expression  exp	=  AviatorEvaluator.compile(expression);
    	  while(i<10000000) {
    		 exp.execute(env);
    		  i++;
         }
    	  long et = System.currentTimeMillis();
    	  System.out.println("耗时:"+((et-st)));
    }
@killme2008
Copy link
Owner

是的,由于一些新特性的引入,导致整体性能受到影响,这块会单独做一个版本优化。目前这个性能暂时也是可以接受的。

@0x2c0d
Copy link
Author

0x2c0d commented Jan 4, 2019

同样的逻辑,执行效率比 fast-el满了 快100倍。感觉还有很大的调优空间。。
//331 ms
@test
public void expParserTest4() {

    //	  String expression = "a+bbb>cccc";
//    	String expression = "111== a || 222 == bbb && 'ABCD' == t &&  3432 == cccc";
    	Random random = new Random();
    //	String expression = "111== a && 222 == bbb && 45 == c &&  3432 == d &&  e > 454 && f <= 343 && string.length(gg) == 0 && hh == 'sdafsdfds' && ii != 'wqerere' && 'qwersdf' == jj ";
    	String expression = "111== a && 222 == bbb &&( 45 == c &&  3432 == d  && ( e > 454 && f <= 343 && string.length(gg) == 0)) && hh == 'sdafsdfds' && ii != 'wqerere' && 'qwersdf' == jj ";
    	  
    	
    	Map<String, Object> map = new HashMap<String, Object>(10);
	    map.put("a",random.nextInt(1000));
	    map.put("b",random.nextInt(1000));
	    map.put("c",random.nextInt(1000));
	    map.put("d",random.nextInt(1000));
	    map.put("e",random.nextInt(1000));
	    map.put("f",random.nextInt(1000));
	    map.put("gg","");
	    map.put("hh",RandomStringUtils.random(16));
	    map.put("ii",RandomStringUtils.random(16));
	    map.put("jj",RandomStringUtils.random(16));
   	   
   	      
    	  long st = System.currentTimeMillis();
    	   int i = 0;

         Expression  exp	=  AviatorEvaluator.compile(expression);
    	
         while(i<10000000) {
  
    	//    Map<String , Object> env  = sss.get(i);
    		 exp.execute(map);
    		  i++;
         }
    	  
    	  long et = System.currentTimeMillis();
    	  System.out.println("耗时ms :"+((et-st)));
    }
    
    /**
     *   6ms 
     * */
    @Test
    public  void expFastEl() {
    	
    	Random random = new Random();
    	
    	String exp = "111== a && 222 == bbb &&( 45 == c &&  3432 == d  && ( e > 454 && f <= 343 && gg == null )) && hh == 'sdafsdfds' && ii != 'wqerere' && 'qwersdf' == jj ";
  	   FelEngine fel = new FelEngineImpl();
    	FelContext ctx = fel.getContext();
     ctx.set("a",random.nextInt(1000));
     ctx.set("b",random.nextInt(1000));
     ctx.set("c",random.nextInt(1000));
     ctx.set("d",random.nextInt(1000));
     ctx.set("e",random.nextInt(1000));
     ctx.set("f",random.nextInt(1000));
     ctx.set("gg","");
     ctx.set("hh",RandomStringUtils.random(16));
     ctx.set("ii",RandomStringUtils.random(16));
     ctx.set("jj",RandomStringUtils.random(16));
    	
      com.greenpineyu.fel.Expression feExpression =  fel.compile(exp,ctx);
  
   long st = System.currentTimeMillis();
 	   int i = 0;      
  	  while(i<10000000) {
  		  feExpression.eval(ctx);
  		  i++;
       }
  	  long et = System.currentTimeMillis();
  	  System.out.println("耗时ms:"+((et-st)));
  }

@killme2008
Copy link
Owner

实现原理是不一样的, aviator 是动态类型,FEL 的编译本质上是生成 java 代码,再调用编译器编译,具体原因参见我很早写的一篇博客

http://www.blogjava.net/killme2008/archive/2011/09/17/358863.html

aviator 有自己的类型系统,而 FEL 是没有的,灵活和性能的平衡。

不过 aviator 要做到 fel 的性能也完全可能,在编译的时候,让用户标注变量类型就可以生成和 java 代码一模一样的字节码,只不过这个优化,我暂时觉的没有太大必要,不准备做一个面面俱到的东西,保持动态性和灵活性,兼顾比解释型更好的性能即可,性能优化会继续做下。

@0x2c0d
Copy link
Author

0x2c0d commented Jan 4, 2019

加油!

@killme2008
Copy link
Owner

顺带说下,fel.compile(exp,ctx) 这里传入的 ctx 其实就是一个类型标注,这样 fel 才会生成正确的 java 代码,如果你传入一个空的 ctx,行为会非常诡异。具体原因可以参见我的那篇博客了。

@killme2008
Copy link
Owner

恩,优化会继续做下,几个比较容易做的:

  1. 减少拆箱和装箱
  2. 公共子表达式提取
  3. 临时对象缩减

@killme2008
Copy link
Owner

发布了 4.1.1 ,对比 4.1.0 有 30% 左右的提升。类型标注的优化,暂时先不做了,做了个原型测试,类型标注下的优化跟原生 java 是一样的,但是会带来比较高的复杂度。

@blueskea
Copy link

确实,从3.3.0升级到4.1.1,随机比大小,耗时差不多是7倍差距。

@killme2008
Copy link
Owner

@blueskea 这个差距不大可能那么大,请给出测试代码。

@killme2008
Copy link
Owner

我写了一个简单测试

package com.googlecode.aviator;

import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import junit.framework.TestCase;

public class SimpleELPerformanceTest extends TestCase {
  public void test_perf() throws Exception {

    Map<String, Object> ctx = new HashMap<String, Object>();


    // AviatorEvaluator.setTrace(true);
    for (int i = 0; i < 10; ++i) {
      perf(ctx);
    }
  }

  private void perf(Map<String, Object> ctx) {
    Expression exp = AviatorEvaluator.compile("a>b");
    long startMillis = System.currentTimeMillis();

    /**
     * time : 255 time : 138 time : 122 time : 120 time : 104 time : 115 time : 138 time : 127 time
     * : 132 time : 131
     *
     */
    final int COUNT = 1000 * 1000;
    for (int i = 0; i < COUNT; ++i) {
      ctx.put("a", ThreadLocalRandom.current().nextLong());
      ctx.put("b", ThreadLocalRandom.current().nextLong());
      ctx.put("c", 5);
      exp.execute(ctx);
    }

    long millis = System.currentTimeMillis() - startMillis;

    System.out.println("time : " + NumberFormat.getInstance().format(millis));
  }
}

跑下来 , 4.1.1 反而更快的。

@killme2008
Copy link
Owner

3.3.0

time : 253
time : 117
time : 106
time : 105
time : 102
time : 107
time : 137
time : 143
time : 112
time : 116

4.1.1

time : 251
time : 136
time : 118
time : 129
time : 107
time : 112
time : 103
time : 122
time : 114
time : 99

两者基本是接近的。

@blueskea
Copy link

blueskea commented Jan 27, 2019

@killme2008 不知道是不是我代码有问题

public class Test {
    static Random random = new Random();
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            long start = System.currentTimeMillis();
            for (int j = 0; j < 100000; j++) {
                AviatorEvaluator.execute("10 > " + random.nextInt());
            }
            System.out.println("Cost Time : " + (System.currentTimeMillis() - start));
        }
    }
}

3.3.0:

Cost Time : 1003
Cost Time : 507
Cost Time : 467
Cost Time : 362
Cost Time : 369

4.1.1:

Cost Time : 6420
Cost Time : 5685
Cost Time : 5682
Cost Time : 5678
Cost Time : 5661

@killme2008
Copy link
Owner

@blueskea  非常感谢反馈,这里是一个 bug,原来这种全部是常量的表达式会直接在编译器折叠成最终结果,在 4.0 版本里这块逻辑处理引入了一个 bug,额外创建了不必要的 class,我马上修复下。

@killme2008
Copy link
Owner

已在最新开发分支 #102 修复,测试来看比 3.3.0 更快

开发分支:

Cost Time : 1313
Cost Time : 239
Cost Time : 265
Cost Time : 201
Cost Time : 331

3.3.0

Cost Time : 1665
Cost Time : 480
Cost Time : 447
Cost Time : 669
Cost Time : 467

@blueskea
Copy link

@killme2008 非常感谢!!

@killme2008
Copy link
Owner

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants