2011-05-19 44 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() 
}