-
Notifications
You must be signed in to change notification settings - Fork 213
Closed
Description
Compared to janino 3.0.11, janino 3.1.2 is ten times slower. Is this expected?
Reproduce
Test case
// For 3.0.11: Total cost 98.523273 ms, average time is 1.970465 ms
// For 3.1.2: Total cost 15863.328650 ms, average time is 317.266573 ms
@Test
public void benchmark() throws Exception {
CompileUnit unit = new CompileUnit("demo.pkg1", "A", (""
+ "package demo.pkg1;\n"
+ "public class A {\n"
+ " public static String hello() { return \"HELLO\"; }\n"
+ "}"
));
// Since janino is not called frequently, we test only 50 times.
int iterNums = 50;
for (int i = 0; i < iterNums; i++) {
JaninoUtils.compile(Thread.currentThread().getContextClassLoader(), unit);
}
long startTime = System.nanoTime();
for (int i = 0; i < iterNums; i++) {
JaninoUtils.compile(Thread.currentThread().getContextClassLoader(), unit);
}
long duration = System.nanoTime() - startTime;
System.out.printf("Total cost %f ms, average time is %f ms",
(double) duration / 1000_000,
(double) duration / iterNums / 1000_000);
}3.1.2
package io.ray.fury.codegen;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.commons.compiler.CompileException;
import org.codehaus.commons.compiler.util.reflect.ByteArrayClassLoader;
import org.codehaus.commons.compiler.util.resource.MapResourceCreator;
import org.codehaus.commons.compiler.util.resource.MapResourceFinder;
import org.codehaus.commons.compiler.util.resource.Resource;
import org.codehaus.janino.ClassLoaderIClassLoader;
import org.codehaus.janino.Compiler;
/**
* A util to compile code to bytecode and create classloader to load generated class.
* Based on org.codehaus.janino.tests.CompilerTest#testInMemoryCompilation,
* see (https://github.com/janino-compiler/janino/issues/52)
*/
public class JaninoUtils {
public static ClassLoader compile(ClassLoader parentClassLoader, CompileUnit... compileUnits) {
final Map<String, byte[]> classes = toBytecode(parentClassLoader, compileUnits);
// Set up a class loader that finds and defined the generated classes.
return new ByteArrayClassLoader(classes, parentClassLoader);
}
public static List<byte[]> compileToBytecode(ClassLoader parentClassLoader, CompileUnit... compileUnits) {
final Map<String, byte[]> classes = toBytecode(parentClassLoader, compileUnits);
List<byte[]> byteCodes = new ArrayList<>(compileUnits.length);
for (CompileUnit unit : compileUnits) {
String key = unit.pkg.replace(".", "/") + "/" + unit.mainClassName + ".class";
byteCodes.add(classes.get(key));
}
return byteCodes;
}
private static Map<String, byte[]> toBytecode(ClassLoader parentClassLoader, CompileUnit... compileUnits) {
MapResourceFinder sourceFinder = new MapResourceFinder();
for (CompileUnit unit : compileUnits) {
String stubFileName = unit.pkg.replace(".", "/") + "/" + unit.mainClassName + ".java";
sourceFinder.addResource(stubFileName, unit.getCode());
Path path = Paths.get(CodeGenerator.getCodeDir(), stubFileName).toAbsolutePath();
try {
path.getParent().toFile().mkdirs();
if (CodeGenerator.deleteCodeOnExit()) {
path.toFile().deleteOnExit();
}
Files.write(path, unit.getCode().getBytes());
} catch (IOException e) {
throw new RuntimeException(String.format("Write code file %s failed", path), e);
}
}
// Storage for generated bytecode
final Map<String, byte[]> classes = new HashMap<>();
// Set up the compiler.
ClassLoaderIClassLoader iClassLoader = new ClassLoaderIClassLoader(parentClassLoader);
Compiler compiler = new Compiler();
compiler.setSourceFinder(sourceFinder);
compiler.setIClassLoader(iClassLoader);
compiler.setClassFileCreator(new MapResourceCreator(classes));
compiler.setClassFileFinder(new MapResourceFinder(classes));
// set debug flag to get source file names and line numbers for debug and stacktrace.
// this is also the default behaviour for javac.
compiler.setDebugSource(true);
compiler.setDebugLines(true);
// Compile all sources
try {
compiler.compile(sourceFinder.resources().toArray(new Resource[0]));
} catch (CompileException | IOException e) {
StringBuilder msgBuilder = new StringBuilder("Compile error: \n");
for (int i = 0; i < compileUnits.length; i++) {
CompileUnit unit = compileUnits[i];
if (i != 0) {
msgBuilder.append('\n');
}
String qualifiedName = unit.pkg + "." + unit.mainClassName;
msgBuilder.append(qualifiedName).append(":\n");
msgBuilder.append(CodeFormatter.format(unit.getCode()));
}
throw new CodegenException(msgBuilder.toString(), e);
}
return classes;
}
}3.0.11
package io.ray.fury.codegen;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.commons.compiler.CompileException;
import org.codehaus.janino.ByteArrayClassLoader;
import org.codehaus.janino.ClassLoaderIClassLoader;
import org.codehaus.janino.Compiler;
import org.codehaus.janino.util.resource.MapResourceCreator;
import org.codehaus.janino.util.resource.MapResourceFinder;
import org.codehaus.janino.util.resource.Resource;
/**
* A util to compile code to bytecode and create classloader to load generated class.
* Based on org.codehaus.janino.tests.CompilerTest#testInMemoryCompilation,
* see (https://github.com/janino-compiler/janino/issues/52)
*/
public class JaninoUtils {
public static ClassLoader compile(ClassLoader parentClassLoader, CompileUnit... compileUnits) {
final Map<String, byte[]> classes = toBytecode(parentClassLoader, compileUnits);
// Set up a class loader that finds and defined the generated classes.
return new ByteArrayClassLoader(classes, parentClassLoader);
}
public static List<byte[]> compileToBytecode(ClassLoader parentClassLoader, CompileUnit... compileUnits) {
final Map<String, byte[]> classes = toBytecode(parentClassLoader, compileUnits);
List<byte[]> byteCodes = new ArrayList<>(compileUnits.length);
for (CompileUnit unit : compileUnits) {
String key = unit.pkg.replace(".", "/") + "/" + unit.mainClassName + ".class";
byteCodes.add(classes.get(key));
}
return byteCodes;
}
private static Map<String, byte[]> toBytecode(ClassLoader parentClassLoader, CompileUnit... compileUnits) {
MapResourceFinder sourceFinder = new MapResourceFinder();
for (CompileUnit unit : compileUnits) {
String stubFileName = unit.pkg.replace(".", "/") + "/" + unit.mainClassName + ".java";
sourceFinder.addResource(stubFileName, unit.getCode());
Path path = Paths.get(CodeGenerator.getCodeDir(), stubFileName).toAbsolutePath();
try {
path.getParent().toFile().mkdirs();
if (CodeGenerator.deleteCodeOnExit()) {
path.toFile().deleteOnExit();
}
Files.write(path, unit.getCode().getBytes());
} catch (IOException e) {
throw new RuntimeException(String.format("Write code file %s failed", path), e);
}
}
// Storage for generated bytecode
final Map<String, byte[]> classes = new HashMap<>();
// Set up the compiler.
ClassLoaderIClassLoader iClassLoader = new ClassLoaderIClassLoader(parentClassLoader);
Compiler compiler = new Compiler(sourceFinder, iClassLoader);
compiler.setClassFileCreator(new MapResourceCreator(classes));
compiler.setClassFileFinder(new MapResourceFinder(classes));
// set debug flag to get source file names and line numbers for debug and stacktrace.
// this is also the default behaviour for javac.
compiler.setDebugSource(true);
compiler.setDebugLines(true);
// Compile all sources
try {
compiler.compile(sourceFinder.resources().toArray(new Resource[0]));
} catch (CompileException | IOException e) {
StringBuilder msgBuilder = new StringBuilder("Compile error: \n");
for (int i = 0; i < compileUnits.length; i++) {
CompileUnit unit = compileUnits[i];
if (i != 0) {
msgBuilder.append('\n');
}
String qualifiedName = unit.pkg + "." + unit.mainClassName;
msgBuilder.append(qualifiedName).append(":\n");
msgBuilder.append(CodeFormatter.format(unit.getCode()));
}
throw new CodegenException(msgBuilder.toString(), e);
}
return classes;
}
}Result
In my macos machine:
For 3.0.11: Total cost 98.523273 ms, average time is 1.970465 ms
For 3.1.2: Total cost 15863.328650 ms, average time is 317.266573 ms
Metadata
Metadata
Assignees
Labels
No labels