2011-05-19 61 views
2

我在写一个用于批量处理excel文件的程序。每行的数据都放入一个映射中,并且文件名和sheetname确定处理提取的数据的脚本。这些脚本没有捆绑到我的程序中,它们甚至没有实现特定接口的类。用缓存提升批量Groovy eval()

这是处理循环逻辑:

excelfile.eachLineOnSheet { line, sheet -> 
    def data = extractData(); 
    def lineprocessorscript = determineLineProcessor(excelfile, sheet); 

    Eval.xy data, outputfile, lineprocessorscript 

} 

当然,这很容易,但对大文件,我想提高性能。首先我缓存了行处理器的代码,这样.groovy文件只能读取一次。

是否有可能通过以某种方式缓存编译的脚本来使Eval.xy更快? 我想保持我的脚本简单,以便不要实现任何接口或东西。

回答

1

@Binil托马斯答案帮助我开始。我看着Groovy的来源,发现GroovyClassLoader有一个内置的缓存机制,但是从Eval方向调用时缓存被关闭:

private Class parseClass(final GroovyCodeSource codeSource) throws CompilationFailedException { 
    // Don't cache scripts 
    return loader.parseClass(codeSource, false); 
} 

为什么不缓存脚本..?这正是我需要的.. :-)所以我写了评估和演示并基于源的东西,这样就出来了:

lineprocessors.each { 

    if(cachedLineProcessorCodes[it] == null) { 
    def gsc = new GroovyCodeSource(new File(it).getText(), it, 'DEFAULT_CODE_BASE') 
    Class cc = gcl.parseClass(gsc, true) 
    cachedLineProcessorCodes[it] = cc 
    } 

    def binding = new Binding() 
    binding.setVariable("x", linedata) 
    binding.setVariable("y", lineProcFiles[it]) 

    def Script sc = InvokerHelper.createScript(cachedLineProcessorCodes[it], binding) 
    sc.run() 

    //Eval.xy linedata, lineProcFiles[it], new File(it).getText() 

} 

在我的情况下,当7900线是由Groovy脚本处理时,运行时间从〜73s减少到〜5s。

+1

这真是太棒了!虽然你不喜欢脚本实现任何特定的接口,但我建议将绑定变量(现在有机会)重命名为比'x'和'y'更有意义的东西。这将有助于将来维护脚本文件,恕我直言。 – 2011-05-20 18:17:21

+0

谢谢,我真的这样做了,只是没有在这里发布这个更改,以保持评论的Eval。xy线和新解决方案100%兼容:-) – jabal 2011-05-21 10:21:07

1

Eval.xy调用GroovyShell.evaluate方法。各种形式的GroovyShell.evaluate都可以归结为以下形式:

public Object evaluate(GroovyCodeSource codeSource) 
     throws CompilationFailedException { 
    Script script = parse(codeSource); 
    script.setBinding(context); 
    return script.run(); 
} 

我还没有尝试过这一点,但你也许可以通过在你的XLS片具有不同绑定调用它的每一行重用Script对象。

+0

+1:谢谢你,你的回答让我得到想要的结果,我贴在了不同的答案。 – jabal 2011-05-20 09:47:54

3

我对其采取

deg gcl = ... // probably new GroovyClassLoader(this.class.classLoader) 
def cache = [:].withDefault{gcl.parseClass(new File(it))} 
... 
lineprocessors.each { 
    cache[it].newInstance([someVariable:1, otherVariable:2] as Binding).run() 
} 

关于你自己的答案
你并不真的需要自己创建GSC,只需加载与保利协鑫的文件一对夫妇的意见。
如果你正在处理脚本,你并不真的需要InvokerHelper,你可以自己实例化它们。
常规语法趁= d

+0

我不知道这些功能,但它们确实令人印象深刻。也从你的回答中学到了很多,谢谢 – jabal 2011-05-21 10:24:49

0

你应该preparse你的脚本,通常这就是最激烈的部分:

String code = '$obj.toString()' 
GroovyShell shell = new GroovyShell() 
Script script = shell.parse(code, 'preparsed') 
items.each {item -> 
    sh.setVariable('$obj', item) 
    Object result = s.run() 
}