Skip to content

Commit

Permalink
improve performance with dynamic invoker
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeloffner committed Dec 17, 2024
1 parent 954a753 commit 3f53869
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 123 deletions.
23 changes: 12 additions & 11 deletions core/src/main/java/lucee/runtime/reflection/Reflector.java
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,13 @@ public static Class[] getClasses(Object[] objs) {
public static Class toReferenceClass(Class c) {
if (c != null && c.isPrimitive()) {
if (c == boolean.class) return Boolean.class;
if (c == int.class) return Integer.class;
if (c == long.class) return Long.class;
if (c == double.class) return Double.class;
if (c == byte.class) return Byte.class;
if (c == short.class) return Short.class;
if (c == char.class) return Character.class;
if (c == int.class) return Integer.class;
if (c == long.class) return Long.class;
if (c == float.class) return Float.class;
if (c == double.class) return Double.class;
}
return c;
}
Expand Down Expand Up @@ -623,14 +623,6 @@ public static ConstructorInstance getConstructorInstance(Class clazz, Object[] a

static int count = 0;

public static Object[] cleanArgs(Object[] args) {
if (args == null) {
return new Object[0];
}
return args;

}

public static Object[] cleanArgsOld(Object[] args) {
if (args == null) {
return new Object[0];
Expand Down Expand Up @@ -892,6 +884,15 @@ public static void checkAccessibility(Class clazz, Key methodName) {
}
}

public static void checkAccessibility(Clazz clazz, Key methodName) {
if (methodName.equals(KeyConstants._exit)) { // TODO better implementation
Class cl = clazz.getDeclaringClass();
if ((cl == System.class || cl == Runtime.class)) { // TODO better implementation
throw new PageRuntimeException(new SecurityException("Calling the exit method is not allowed"));
}
}
}

/*
* private static void checkAccesibilityx(Object obj, Key methodName) {
* if(methodName.equals(SET_ACCESSIBLE) && obj instanceof Member) { if(true) return; Member
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ private Class<?> loadClass(String name, boolean resolve, boolean loadFromFS) thr
}

private Class<?> _loadClass(String name, byte[] barr) {
// print.e(">>>>" + name);
Class<?> clazz = defineClass(name, barr, 0, barr.length);
if (clazz != null) {
loadedClasses.put(name, "");
Expand Down Expand Up @@ -273,6 +272,7 @@ public boolean hasClass(String className) {
}

public boolean isClassLoaded(String className) {
if (loadedClasses.containsKey(className)) return true;
return findLoadedClass(className) != null;
}

Expand Down
171 changes: 130 additions & 41 deletions core/src/main/java/lucee/transformer/dynamic/DynamicInvoker.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
Expand Down Expand Up @@ -31,7 +34,6 @@
import org.objectweb.asm.Type;

import lucee.aprint;
import lucee.commons.digest.HashUtil;
import lucee.commons.io.SystemUtil;
import lucee.commons.io.log.Log;
import lucee.commons.io.res.Resource;
Expand All @@ -42,6 +44,7 @@
import lucee.commons.lang.SystemOut;
import lucee.loader.engine.CFMLEngineFactory;
import lucee.runtime.exp.PageException;
import lucee.runtime.op.Caster;
import lucee.runtime.reflection.Reflector;
import lucee.runtime.type.Collection.Key;
import lucee.runtime.type.KeyImpl;
Expand All @@ -55,6 +58,7 @@
import lucee.transformer.dynamic.meta.FunctionMember;
import lucee.transformer.dynamic.meta.LegacyMethod;
import lucee.transformer.dynamic.meta.Method;
import lucee.transformer.dynamic.meta.dynamic.ClazzDynamic;

public class DynamicInvoker {

Expand Down Expand Up @@ -143,28 +147,24 @@ public Clazz getClazz(Class<?> clazz, boolean useReflection) {
return Clazz.getClazz(clazz, root, log, useReflection);
}

/*
* private static double getClass = 0; private static double match = 0; private static double types
* = 0; private static double getDeclaringClass = 0; private static double lclassPath = 0; private
* static double pathName = 0; private static double clsLoader = 0; private static double
* loadInstance = 0; private static int count = 0;
*/
private static double getClass = 0;
private static double match = 0;
private static double getDeclaringClass = 0;
private static double pathName = 0;
private static double clsLoader = 0;
private static double loadInstance = 0;

public Pair<FunctionMember, Object> createInstance(Class<?> clazz, Key methodName, Object[] arguments, boolean convertComparsion) throws NoSuchMethodException, IOException,
UnmodifiableClassException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, PageException {

/*
* count++; if ((count % 500) == 0) { print.e("-------------------"); print.e("getClass:" +
* getClass); print.e("match:" + match); print.e("types:" + types); print.e("getDeclaringClass:" +
* getDeclaringClass); print.e("lclassPath:" + lclassPath); print.e("pathName:" + pathName);
* print.e("clsLoader:" + clsLoader); print.e("loadInstance:" + loadInstance); }
*/

// double start = SystemUtil.millis();
boolean isConstr = methodName == null;
Clazz clazzz = getClazz(clazz);

// getClass += (SystemUtil.millis() - start);
// Clazz clazzz = getClazz(clazz);
Clazz clazzz = ClazzDynamic.getInstance(clazz, root, log);
// Clazz clazzz = new ClazzReflection(clazz);
// getClass -= start;
// start = SystemUtil.millis();
// getClass += start;

lucee.transformer.dynamic.meta.FunctionMember fm = null;
lucee.transformer.dynamic.meta.Method method = null;
Expand All @@ -176,36 +176,28 @@ public Pair<FunctionMember, Object> createInstance(Class<?> clazz, Key methodNam
// Clazz clazz, final Collection.Key methodName, final Object[] args, boolean convertArgument
fm = method = Clazz.getMethodMatch(clazzz, methodName, arguments, true, convertComparsion);
}
// match += (SystemUtil.millis() - start);
// match -= start;
// start = SystemUtil.millis();
// match += start;

// types += (SystemUtil.millis() - start);
// start = SystemUtil.millis();
clazz = fm.getDeclaringClass(); // we wanna go as low as possible, to be as open as possible also this avoid not allow to access

// getDeclaringClass += (SystemUtil.millis() - start);
// getDeclaringClass -= start;
// start = SystemUtil.millis();
// getDeclaringClass += start;

StringBuilder sbClassPath = new StringBuilder();
sbClassPath.append(clazz.getName().replace('.', '/')).append('/').append(isConstr ? "____init____" : fm.getName());
if (fm.getArgumentCount() > 0) {
StringBuilder sbArgs = new StringBuilder();
for (String arg: fm.getArguments()) {
sbArgs.append(':').append(arg);
}
sbClassPath.append('_').append(HashUtil.create64BitHashAsString(sbArgs, Character.MAX_RADIX));
}
// lclassPath += (SystemUtil.millis() - start);
// start = SystemUtil.millis();
String className = fm.getClassName();

String classPath = Clazz.getPackagePrefix() + sbClassPath.toString();// StringUtil.replace(sbClassPath.toString(), "javae/lang/", "java_lang/", false);
String className = classPath.replace('/', '.');

// pathName += (SystemUtil.millis() - start);
// pathName -= start;
// start = SystemUtil.millis();
// pathName += start;

DynamicClassLoader loader = getCL(clazz);
// clsLoader += (SystemUtil.millis() - start);

// clsLoader -= start;
// start = SystemUtil.millis();
// clsLoader += start;

if (loader.hasClass(className)) {
try {
return new Pair<FunctionMember, Object>(fm, loader.loadInstance(className));
Expand All @@ -215,9 +207,9 @@ public Pair<FunctionMember, Object> createInstance(Class<?> clazz, Key methodNam
// simply ignore when fail
}
// finally {
// loadInstance += (SystemUtil.millis() - start);
// loadInstance -= start;
// start = SystemUtil.millis();

// loadInstance += start;
// }
}
synchronized (SystemUtil.createToken("dyninvocer", className)) {
Expand All @@ -226,7 +218,7 @@ public Pair<FunctionMember, Object> createInstance(Class<?> clazz, Key methodNam
ClassWriter cw = ASMUtil.getClassWriter();
MethodVisitor mv;
String abstractClassPath = "java/lang/Object";
cw.visit(ASMUtil.getJavaVersionForBytecodeGeneration(), Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, classPath,
cw.visit(ASMUtil.getJavaVersionForBytecodeGeneration(), Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, fm.getClassPath(),
"Ljava/lang/Object;Ljava/util/function/BiFunction<Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;>;", "java/lang/Object",
new String[] { "java/util/function/BiFunction" });
// Constructor
Expand Down Expand Up @@ -428,12 +420,109 @@ public static Type[] getArgumentTypes(Constructor<?> constructor) {
return Type.getArgumentTypes(descriptor.toString());
}

public static void main(String[] args) throws Exception {
public static class TestMule {
public void test(int x) {

}
}

public Pair<FunctionMember, Object> createInstance2(Class<?> clazz, Key methodName, Object[] arguments, boolean convertComparsion) {

return null;
}

public static void main(String[] args) throws Throwable {
System.setProperty("lucee.allow.reflection", "false");
Resource classes = ResourcesImpl.getFileResourceProvider().getResource("/Users/mic/tmp8/classes/");
ResourceUtil.deleteContent(classes, null);
DynamicInvoker e = new DynamicInvoker(classes);

DynamicInvoker.getInstance(classes);
{
int rounds = 6;
int max = 500000;
long dynamicInvoker = Long.MAX_VALUE;
long dynamicInvoker2 = Long.MAX_VALUE;
long reflection = Long.MAX_VALUE;
long direct = Long.MAX_VALUE;
long methodHandle = Long.MAX_VALUE;
long tmp;
TestMule tm = new TestMule();
Class<? extends TestMule> clazz = tm.getClass();
Class[] cargs = new Class[] { int.class };
// reflection
for (int i = 0; i < rounds; i++) {
long start = System.currentTimeMillis();
for (int y = 0; y < max; y++) {
clazz.getMethod("test", cargs).invoke(tm, new Object[] { 1 });
}
tmp = System.currentTimeMillis() - start;
if (tmp < reflection) reflection = tmp;
}

// invokeInstanceMethod
for (int i = 0; i < rounds; i++) {
long start = System.currentTimeMillis();
for (int y = 0; y < max; y++) {
e.invokeInstanceMethod(tm, "test", new Object[] { 1 }, false);
}
tmp = System.currentTimeMillis() - start;
if (tmp < dynamicInvoker) dynamicInvoker = tmp;
}

// invokeInstanceMethod
for (int i = 0; i < rounds; i++) {
long start = System.currentTimeMillis();
for (int y = 0; y < max; y++) {
Reflector.getMethod(clazz, "test", cargs).invoke(tm, new Object[] { 1 });
}
tmp = System.currentTimeMillis() - start;
if (tmp < dynamicInvoker2) dynamicInvoker2 = tmp;
}

// MethodHandles
MethodHandles.Lookup lookup = MethodHandles.lookup();
for (int i = 0; i < rounds; i++) {
long start = System.currentTimeMillis();
for (int y = 0; y < max; y++) {
MethodType methodType = MethodType.methodType(void.class, int.class);
MethodHandle testHandle = lookup.findVirtual(clazz, "test", methodType);
testHandle.invoke(tm, 1);

}
tmp = System.currentTimeMillis() - start;
if (tmp < methodHandle) methodHandle = tmp;
}

// direct
for (int i = 0; i < rounds; i++) {
long start = System.currentTimeMillis();
for (int y = 0; y < max; y++) {
tm.test(1);
}
tmp = System.currentTimeMillis() - start;
if (tmp < direct) direct = tmp;
}

aprint.e("dynamicInvoker2:" + dynamicInvoker2);
aprint.e("dynamicInvoker:" + dynamicInvoker);
aprint.e("reflection:" + reflection);
aprint.e("methodHandle:" + reflection);
aprint.e("direct:" + direct);

aprint.e("-------------------");
double total = getClass + match + getDeclaringClass + pathName + clsLoader + loadInstance;

aprint.e(((int) getClass) + ":" + Caster.toIntValue(100d / total * getClass) + "% :getClass");
aprint.e(((int) match) + ":" + Caster.toIntValue(100d / total * match) + "% :match");
aprint.e(((int) getDeclaringClass) + ":" + Caster.toIntValue(100d / total * getDeclaringClass) + "% :getDeclaringClass");
aprint.e(((int) pathName) + ":" + Caster.toIntValue(100d / total * pathName) + "% :pathName");
aprint.e(((int) clsLoader) + ":" + Caster.toIntValue(100d / total * clsLoader) + "% :clsLoader");
aprint.e(((int) loadInstance) + ":" + Caster.toIntValue(100d / total * loadInstance) + "% :loadInstance");

if (true) return;
}

{
List<Method> methods;

Expand Down
Loading

0 comments on commit 3f53869

Please sign in to comment.