Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

About this user

Sam McCall

« Newer Snippets
Older Snippets »
Showing 1-2 of 2 total  RSS 

Microbenchmark for Bridge vs reflection vs direct access

   1  import java.lang.reflect.Method;
   2  
   3  public class TimingTest {
   4  	public interface IFoo { public void foo(); }
   5  	public interface PubFoo { public void foo(); }
   6  	public static class Impl implements PubFoo { public void foo(){} }
   7  
   8  	static final int COUNT = 10000000;
   9  	public static void main(String[] args) throws Exception {
  10  		Impl impl = new Impl();
  11  		int count;
  12  
  13  		startTiming("Bridge (including BCEL)");
  14  		IFoo ifoo = Bridge.expose(impl, IFoo.class);
  15  		stopTiming();
  16  		startTiming("Bridge (BCEL loaded)");
  17  		Bridge.expose(impl, PubFoo.class);
  18  		stopTiming();
  19  
  20  		System.out.println(COUNT+" virtual invocations, warm");
  21  
  22  		startTiming("Direct");
  23  		for(int i=0; i<COUNT; i++)
  24  			impl.foo();
  25  		stopTiming();
  26  
  27  		Method m = impl.getClass().getMethod("foo",new Class[0]);
  28  		startTiming("Reflection");
  29  		for(int i=0; i<COUNT; i++)
  30  			m.invoke(impl);
  31  		stopTiming();
  32  
  33  		startTiming("Bridge");
  34  		for(int i=0; i<COUNT; i++)
  35  			ifoo.foo();
  36  		stopTiming();
  37  
  38  
  39  		System.out.println("1 virtual invocation on "+COUNT+" objects");
  40  
  41  		startTiming("Direct");
  42  		for(int i=0; i<COUNT; i++)
  43  			impl.foo();
  44  		stopTiming();
  45  
  46  		startTiming("Reflection");
  47  		Method tmpM = null;
  48  		Class[] no_args = new Class[0];
  49  		for(int i=0; i<COUNT; i++) {
  50  			if(tmpM == null || tmpM.getDeclaringClass() != impl.getClass())
  51  				tmpM = impl.getClass().getMethod("foo",no_args);
  52  			tmpM.invoke(impl);
  53  		}
  54  		stopTiming();
  55  
  56  		startTiming("Bridge");
  57  		for(int i=0; i<COUNT; i++) {
  58  			IFoo temp = Bridge.expose(impl, IFoo.class);
  59  		}
  60  		stopTiming();
  61  
  62  		
  63  		System.out.println(COUNT+" interface invocations, warm");
  64  		PubFoo pfoo = (PubFoo)impl;
  65  		Method mi = impl.getClass().getInterfaces()[0].getMethod("foo", new Class[]{});
  66  		IFoo iifoo = Bridge.expose(impl, PubFoo.class, IFoo.class);
  67  
  68  		startTiming("Direct");
  69  		for(int i=0; i<COUNT; i++)
  70  			pfoo.foo();
  71  		stopTiming();
  72  
  73  		startTiming("Reflection");
  74  		for(int i=0; i<COUNT; i++)
  75  			mi.invoke(impl);
  76  		stopTiming();
  77  
  78  		startTiming("Bridge");
  79  		for(int i=0; i<COUNT; i++)
  80  			iifoo.foo();
  81  		stopTiming();
  82  		
  83  	}
  84  	static long timer;
  85  	static void startTiming(String task) {
  86  		System.out.printf("  * %12s - ", task);
  87  		System.out.flush();
  88  		timer = System.currentTimeMillis();
  89  	}
  90  	static void stopTiming() {
  91  		long time = System.currentTimeMillis() - timer;
  92  		System.out.println(time+" ms");
  93  	}
  94  }

Exposing an interface at runtime using a proxy class

Force an object expose an interface at runtime, through a generated proxy class. It must implement all required methods.
Requires BCEL.

UPDATED: does verification at bridge generation time.
expose(Object, Class) will search for the correct public target type to use.
UPDATED: performance hacks.

Example usage:
   1  public class BridgeTest {
   2    public interface CharList {
   3      public int length();
   4      public char charAt(int i);
   5    }
   6    void print(CharList l) {
   7      for(int i=0; i<l.length(); i++)
   8        System.out.print(l.charAt(i));
   9    }
  10    public static void main(String args[]) {
  11      CharList list = Bridge.expose("Hello world\n", CharList.class);
  12      print(list);
  13    }
  14  }


Bridge.java:
   1  /* Make an object expose an interface at runtime */
   2  
   3  import org.apache.bcel.generic.*;
   4  import org.apache.bcel.Constants;
   5  
   6  import java.lang.reflect.Method;
   7  import java.util.*;
   8  
   9  public abstract class Bridge<C,I> {
  10  	protected C __target; // the implementation of the interface
  11  	private final void __init(Object target) { this.__target = (C)target; }
  12  
  13  	// cache already-generated bridges
  14  	private static HashMap<Class, HashMap<Class, Class>> cache 
  15  		= new HashMap<Class, HashMap<Class, Class>>();
  16  	// allow injection of classes from byte arrays
  17  	private static InjectingClassLoader loader = new InjectingClassLoader();
  18  
  19  	private static final void cacheSet(Class key1, Class key2, Class value) {
  20  		HashMap<Class,Class> intermediate = cache.get(key1);
  21  		if(intermediate == null)
  22  			cache.put(key1, intermediate = new HashMap<Class,Class>());
  23  		intermediate.put(key2, value);
  24  	}
  25  	private static final Class cacheGet(Class key1, Class key2) {
  26  		HashMap<Class,Class> intermediate = cache.get(key1);
  27  		if(intermediate == null)
  28  			return null;
  29  		return intermediate.get(key2);
  30  	}
  31  
  32  	// returns [ [ifaceMethods...] [fromMethods...] ]
  33  	private static Method[][] getMapping(Class from, Class iface) throws NoSuchMethodException,IllegalAccessException {
  34  		if(!iface.isInterface())
  35  			throw new IllegalArgumentException(iface.getName()+" is not an interface");
  36  		if(!java.lang.reflect.Modifier.isPublic(from.getModifiers()))
  37  			throw new IllegalAccessException(from.getName()+" is not public");
  38  		java.lang.reflect.Method[][] map = new java.lang.reflect.Method[2][];
  39  		map[0] = iface.getMethods();
  40  		map[1] = new java.lang.reflect.Method[map[0].length];
  41  
  42  		for(int i=0;i<map[0].length;i++) {
  43  			try {
  44  				Method match = from.getMethod(map[0][i].getName(), map[0][i].getParameterTypes());
  45  				if(!map[0][i].getReturnType().isAssignableFrom(match.getReturnType()))
  46  					throw new NoSuchMethodException("Return type "+match.getReturnType().getName()+
  47  						" of "+toString(match)+" is not compatible with return type "+
  48  						map[0][i].getReturnType().getName()+" of "+toString(map[0][i]));
  49  				map[1][i] = match;
  50  			} catch (NoSuchMethodException e) {
  51  				throw new NoSuchMethodException("Couldn't find "+toString(map[0][i])+" in "+
  52  					from.getName());
  53  			}			
  54  		}
  55  		return map;
  56  	}
  57  	private static String toString(Method m) {
  58  		StringBuffer sb = new StringBuffer(m.getDeclaringClass().getName());
  59  		sb.append(".").append(m.getName()).append("(");
  60  		boolean first=true;
  61  		for(Class c : m.getParameterTypes()) {
  62  			if(!first)
  63  				sb.append(", ");
  64  			sb.append(c.getName());	
  65  			first=false;
  66  		}
  67  		sb.append(")");
  68  		return sb.toString();
  69  	}
  70  
  71  	private static <C,I> Class<? extends Bridge<C,I>> create(Class<C> from, Class<I> iface) {
  72  		try {
  73  			Method[][] map = getMapping(from, iface);
  74  
  75  			String bridgeName = "Bridge_"+from.getSimpleName()+"_"+iface.getSimpleName();
  76  			// public class Bridge_Thing_Mungible extends Bridge<Thing,Mungible> implements Mungible
  77  			ClassGen cg = new ClassGen(
  78  				bridgeName,
  79  				Bridge.class.getName(), // superclass name
  80  				"<generated>", // source file name
  81  				Constants.ACC_PUBLIC | Constants.ACC_FINAL | Constants.ACC_SUPER, // flags
  82  				new String [] { iface.getName() } // interfaces
  83  			);
  84  
  85  			ConstantPoolGen cpg = cg.getConstantPool();
  86  			InstructionFactory factory = new InstructionFactory(cg, cpg);
  87  
  88  			ReferenceType targetType = (ReferenceType)Type.getType(from);
  89  
  90  			for(int i=0; i<map[0].length; i++) {
  91  				Method imeth = map[0][i];
  92  				Method fmeth = map[1][i];
  93  
  94  				Type 	ireturnType = Type.getType(imeth.getReturnType()),
  95  					freturnType = Type.getType(fmeth.getReturnType());
  96  				Class[]	iargs = imeth.getParameterTypes(), 
  97  					fargs = fmeth.getParameterTypes();
  98  				Type[] 	iargsT = new Type[iargs.length],
  99  					fargsT = new Type[fargs.length];
 100  				String[] argNames = new String[iargs.length];
 101  				for(int j=0; j<iargs.length; j++) {
 102  					iargsT[j] = Type.getType(iargs[j]);
 103  					fargsT[j] = Type.getType(fargs[j]);
 104  					argNames[j] = "arg"+i;
 105  				}
 106  
 107  				InstructionList il = new InstructionList();
 108  				// public int munge(Object arg0, int arg1) {
 109  				MethodGen mg = new MethodGen(
 110  					Constants.ACC_PUBLIC,
 111  					ireturnType,
 112  					iargsT,
 113  					argNames,
 114  					imeth.getName(),
 115  					bridgeName,
 116  					il, cpg);
 117  
 118  				// (Thing)this.__target
 119  				il.append(new ALOAD(0));
 120  				il.append(factory.createFieldAccess(
 121  					Bridge.class.getName(), 
 122  					"__target", 
 123  					Type.OBJECT, 
 124  					Constants.GETFIELD));
 125  				il.append(factory.createCheckCast(targetType));
 126  				
 127  				// .munge(arg0, arg1);
 128  				int position = 1;
 129  				for(int j=0;j<iargs.length;j++) {
 130  					il.append(factory.createLoad(iargsT[j], position));
 131  					position += iargsT[j].getSize();
 132  				}
 133  				il.append(factory.createInvoke(
 134  					from.getName(), 
 135  					fmeth.getName(), 
 136  					freturnType, 
 137  					fargsT, 
 138  					from.isInterface() ? Constants.INVOKEINTERFACE : Constants.INVOKEVIRTUAL));
 139  
 140  				// return (last result, if any)
 141  				il.append(factory.createReturn(ireturnType));
 142  
 143  				mg.setMaxStack();
 144  				cg.addMethod(mg.getMethod());
 145  				il.dispose(); // Allow instruction handles to be reused
 146  			}
 147  
 148  			// public Bridge_Thing_Mungible() { super(); }
 149  			cg.addEmptyConstructor(Constants.ACC_PUBLIC);
 150  
 151  			try { cg.getJavaClass().dump("proxy.class"); } catch(Exception e) { throw new RuntimeException(e); }
 152  
 153  			byte[] classData = cg.getJavaClass().getBytes();
 154  			Class c = loader.load(cg.getClassName(), classData);
 155  			return (Class<Bridge<C,I>>)c;
 156  		} catch (IllegalAccessException e) { 
 157  			throw new BridgeFailure(from, iface, e); 
 158  		} catch (NoSuchMethodException e) { 
 159  			throw new BridgeFailure(from, iface, e); 
 160  		}
 161  	}
 162  	private static <C,I> Class<? extends Bridge<C,I>> get(Class<C> from, Class<I> iface) {
 163  //		Pair<Class,Class> key = new Pair<Class,Class>(from, iface);
 164  		Class<? extends Bridge<C,I>> bridgeClass = (Class<? extends Bridge<C,I>>)cacheGet(from, iface);
 165  		if(bridgeClass == null) {
 166  			bridgeClass = create(from, iface);
 167  			cacheSet(from, iface, bridgeClass);
 168  		}
 169  		return bridgeClass;
 170  	}
 171  
 172  	// Expose interface iface by creating a proxy. 
 173  	// the type of target must be from, a subclass of from, or a class implementing from.
 174  	// from must be public, and must expose all methods in iface.
 175  	public static <I> I expose(Object target, Class from, Class<I> iface) {
 176  		try {
 177  			Class<? extends Bridge<?,I>> c = Bridge.get(from, iface);
 178  			Bridge<?,I> proxy = c.newInstance();
 179  			proxy.__init(target);
 180  			return (I)proxy;
 181  		} catch (InstantiationException e) {
 182  			throw new RuntimeException(e);
 183  		} catch (IllegalAccessException e) {
 184  			throw new RuntimeException(e);
 185  		}
 186  	}
 187  	
 188  	// Expose interface iface by creating a proxy. 
 189  	// Tries expose(target, target.getClass(), iface) first.
 190  	// then works up the class hierarchy. If doesn't find a public superclass 
 191  	// that exposes all methods, it tries interfaces.
 192  	// if you know in advance the class or interface that exposes the needed methods,
 193  	// use expose(target, Class.forName("ExposingClass"), iface).
 194  	public static <I> I expose(Object target, Class<I> iface) {
 195  		Class tClass = target.getClass();
 196  
 197  		Class from = tClass;
 198  		do {			
 199  			try {
 200  				I result = expose(target, from, iface);
 201  				if(tClass != from) // if tClass == tFrom then get() sets the cache
 202  					cacheSet(tClass, iface, result.getClass());
 203  				return result;
 204  			} catch (BridgeFailure e) {
 205  				Throwable cause = e.getCause();
 206  				if(cause instanceof NoSuchMethodException) {
 207  					// have traced superclass up until the interface isn't satisfied.
 208  					// try interfaces and then give up.
 209  					// in the case of a private implementation of a public interface, 
 210  					// this allows you to subset the interface.
 211  					Class[] targetIfaces = target.getClass().getInterfaces();
 212  					for(Class targetIface : targetIfaces) 
 213  						try {
 214  							I result = expose(target, targetIface, iface);
 215  							cacheSet(tClass, iface, result.getClass()); // perf hack
 216  							return result;
 217  							// ignore, reported exception is that for class hierarchy.
 218  						} catch (BridgeFailure ex) {} 
 219  					throw new BridgeFailure(target, iface, e);
 220  				} else if(!(cause instanceof IllegalAccessException))
 221  					throw e;
 222  			}
 223  			from = from.getSuperclass();
 224  		} while(from != null);
 225  		throw new RuntimeException("Object "+target+" ("+target.getClass().getName()+") has no public superclass");
 226  	}
 227  }
 228  class BridgeFailure extends RuntimeException {
 229  	public BridgeFailure(Class from, Class iface, Exception cause) {
 230  		super("Could not map class "+from.getName()+" to interface "+iface.getName());
 231  		initCause(cause);
 232  	}
 233  	public BridgeFailure(Object target, Class iface, Exception cause) {
 234  		super("Could not map first public supertype of " + target +
 235  			" (" + target.getClass().getName() + ")," +
 236  			" or any implemented interface, to interface " + iface.getName());
 237  		initCause(cause);
 238  	}
 239  }
 240  // ClassLoader implementation to let you load classes from byte arrays
 241  class InjectingClassLoader extends ClassLoader { 
 242  	public InjectingClassLoader() {
 243  		super(InjectingClassLoader.class.getClassLoader());
 244  	}
 245  	public Class load(String name, byte[] buffer) {  
 246  		return defineClass(name, buffer, 0, buffer.length);  
 247  	}  
 248  }
« Newer Snippets
Older Snippets »
Showing 1-2 of 2 total  RSS