10

我正在用 Java 构建一个框架,它将监听事件,然后在 Jython 中处理它们。不同的事件类型将被发送到不同的脚本。

由于调用 PythonInterpreter.exec() 时 jython 需要相当长的时间来编译脚本,因此我必须预编译脚本。我正在按照以下方式进行操作:

// initialize the script as string (would load it from file in final version)
String script = "print 'foo'";
// get the compiled code object
PyCode compiled = org.python.core.__builtin__.compile( script, "<>", "exec" );

PyCode 编译的对象将被推送到存储库并在事件进入时使用

PythonInterpreter pi = new PythonInterpreter();
pi.set( "variable_1", "value_1");
pi.set( "variable_x", "value_x");
pi.exec( compiled );

现在我的难题 - 可能会同时发生多个特定类型的事件 - 因此同时运行多个脚本实例。

几乎所有的脚本都可能保持短暂的生命——最多 100 行,没有循环。数量和频率是完全随机的(用户生成的事件),每个事件类型可能是每秒 0 到大约 200 个。

最好的方法是什么?我正在研究几种可能性:

  1. 在触发事件点使用同步 - 这将防止同一脚本的多个实例,但事件也不会像应有的那样快速处理
  2. 通过克隆原始 PyCode 对象以某种方式填充相同类型的脚本池 - 最大的问题可能是优化池大小
  3. 在需要时从父对象动态克隆脚本对象,然后在 exec() 完成时将其丢弃 - 这样从编译中删除了滞后,但它仍然存在于克隆方法中

数字 2 和 3 的组合可能是最好的 - 创建动态池大小?

那么,有什么想法吗?;)

4

2 回答 2

3

It is a pity that PyCode instances aren't immutable (there are a lot of public members on the classes).

You can precompile a reusable script using this code:

// TODO: generate this name
final String name = "X";
byte[] scriptBytes = PyString.to_bytes(script);
CompilerFlags flags = Py.getCompilerFlags();
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
Module.compile(parser.parse(new ByteArrayInputStream(scriptBytes), "exec",
    "<>", flags), ostream, name, "<>", false, false, false, flags);
byte[] buffer = ostream.toByteArray();
Class<PyRunnable> clazz = BytecodeLoader.makeClass(name, null, buffer);
final Constructor<PyRunnable> constructor = clazz
    .getConstructor(new Class[] { String.class });

You can then use the constructor to produce PyCode instances for the script whenever you need one:

 PyRunnable r = constructor.newInstance(name);
 PyCode pc = r.getMain();

I would be the first to admit that this is not a good way of doing things and probably speaks volumes about my inexperience with Jython. However, it is significantly faster than compiling every time. The code works under Jython 2.2.1, but won't compile under Jython 2.5 (nor will yours).

于 2009-08-13T17:43:27.653 回答
1

PythonInterpreter 很昂贵,此代码将只使用一个。

#action.py
def execute(filename, action_locals):
    #add caching of compiled scripts here
    exec(compile(open(filename).read(), filename, 'exec'), action_locals)

//class variable, only one interpreter
PythonInterpreter pi;

//run once in init() or constructor
pi = new PythonInterpreter();//could do more initialization here
pi.exec("import action");

//every script execution
PyObject pyActionRunner = pi.eval("action.execute");
PyString pyActionName = new PyString(script_path);
PyDictionary pyActionLocals = new PyDictionary();
pyActionLocals.put("variable_1", "value_1");
pyActionLocals.put("variable_x", "value_x")
pyActionRunner.__call__(pyActionName, pyActionLocals);

#example_script.py
print variable_1, variable_x
于 2009-11-26T23:07:10.573 回答