2

我试图通过在 Java 应用程序中调用 groovy eval(string) 方法来追踪性能问题的根源。如果我执行以下代码;

String pattern = "test = ['one','two','three']";
engine.eval(pattern)

它几乎可以立即运行(0 到 1 毫秒)

但是,如果我说以下

String first = "['one','two','three']";
String pattern = "test = " + first; // "identical" String to first approach 
engine.eval(pattern)

运行大约需要 30+ 毫秒。

更糟糕的是,在数千次调用之后,它会高达 60 - 70 毫秒,尽管我并不关心这两个实现之间的时间增量。

关于为什么会发生这种情况的任何解释/关于如何避免这种情况的建议?我怀疑它与 Java 和/或 Groovy 编译器有关,我已经开始研究 compile() 方法,但如果有一种简单的方法可以使现有代码工作,我更喜欢它(更少的东西可以改变它方法)。

4

4 回答 4

1

如果你看看https://github.com/groovy/groovy-core/blob/master/subprojects/groovy-jsr223/src/main/java/org/codehaus/groovy/jsr223/GroovyScriptEngineImpl.java?source=cc你将找到用于将字符串映射到类的缓存。问题是 ManagedConcurrentMap 基本上是一个身份哈希图,这意味着如果你一遍又一遍地使用相同的字符串,它会很快,因为后续运行会跳过编译。问题版本每次都会创建一个新字符串,因此每次都需要编译,导致每次都生成一个新类。

关于如何避免这种情况:创建基于哈希的缓存

于 2013-10-09T14:39:49.247 回答
0

eval任何语言中的 's 都是出了名的慢,并且经常被建议远离(“eval is evil”)。但是,在您的代码中,似乎更多的是字符串连接导致减速,而不是 eval 本身。

尽管如此,几毫秒不应该引起关注——您是否确定这会导致您的代码出现性能瓶颈?对代码进行微观管理可能会在未来导致错误和不清楚的代码,这可能会产生更糟糕的影响。

于 2013-10-04T17:41:01.993 回答
0

最终我们认识到

eval("['one','two','three']")

完全返回,所以

Object test = engine.eval(first)

相当于

engine.eval("test = " + "['one','two','three']") // see text above for exact syntax
Object test = engine.get("test")

然而它根本没有时间有效地运行。

我仍然很好奇为什么使模式动态化会对 eval() 的性能产生如此巨大的影响,但我怀疑这需要对 Java 和/或 groovy 运行时元素有深刻理解的人来阐明它.

感谢所有试图提供帮助的人。问候,

附言。

于 2013-10-07T15:37:41.673 回答
0

你如何测量调用的时间?如果可能的话,我会测量每个部分(String 构建和 eval())以确定哪些部分花费时间,哪些部分会随着迭代次数的增加而增加。既然您说时间随着迭代次数的增加而增加,请查看垃圾收集。第一种情况使用单个字符串 - 在后一种情况下,您每次迭代都构建一个新字符串,因此会消耗内存。您可能会遇到堆限制。

一个非常有用的工具是 VisualVM。http://visualvm.java.net/

于 2013-10-04T22:00:06.610 回答