Skip to content

Performance Considerations

孙得强 edited this page Apr 16, 2021 · 8 revisions

Jep is used on production systems that run significant amounts of Python in mission critical servers. In these situations there are some recommended best practices.

Memory management

You can leak Python objects (or Java objects in Python!) if you continually create new variables in an interpreter's global scope. There's a few different techniques you can use to ensure this doesn't happen. In no particular order:

  • Execute most of your Python code in functions, so any variables created exist only temporarily in local scope. The Python garbage collector will take care of variables after they go out of scope. For example:
def foo():
    args = get_args()
    values = pre_process(args)
    doMath(values)
interp.exec("foo()");

where foo() does all the work. args and values will go out of scope and be automatically cleaned up.

  • Delete any variables created by jep.setValue(String name, Object var); once you are done with them. For example:
interp.set("x", javaObj);
interp.exec("result = foo(x)");
Object result = interp.getValue("result");
interp.exec("del x");
interp.exec("del result");

You can also shortcut this with Python code like:

for g in globals():
   if some_condition(g):
      del g
  • Close the Jep interpreters when you are done with them. This is easiest using Java try-with-resource, for example:
try(Jep interp = new SharedInterpreter()) {
   // runScript, invoke, exec, and other fun things
}

Speed

Starting and stopping Python interpreters, while fast, is relatively slow in comparison to using an already initialized interpreter. This becomes even more noticeable as the number of modules that get imported into the interpreter grows. In these cases you should strongly consider retaining your Interpreter instances and reusing them.

See also Jep and the GIL.

Efficiency

Jep requires that the thread that initializes the interpreter is the same thread for any operations on the interpreter. If you want to reuse Interpreters for speed or stateful reasons, and you also want to multithread your calls to Python, a useful technique is to pool Interpreters. An accompanying factory class can create and initialize your Interpreters on demand for the pool, and you can limit the number of threads in the pool to ensure that the Python components do not overwhelm the rest of the system.

Clone this wiki locally