graphql-java icon indicating copy to clipboard operation
graphql-java copied to clipboard

WIP - Bytecode based data fetchers

Open bbakerman opened this issue 3 years ago • 4 comments

This PR is was inspired from some work done on https://github.com/jord1e/graphql-java-asm-datafetcher

While it does not use any of the code, it uses the some of the idea.

That is could you dynamically generate a class that helps you fetch directly via method calls instead of reflection Method.invoke() calls

This uses JavaAssist to generate a peer class for each source class.

The generated class looks much like this

public class GeneratedUniqueName implements ByteCodeFetcher {

public Object fetch(Object sourceObject, String propertyName) {
   if (sourceObject == null) { return null; }
   graphql.schema.bytecode.SettersAndVoidPojo source = (graphql.schema.bytecode.SettersAndVoidPojo) sourceObject;
   if("name".equals(propertyName)) {
      return source.getName();
   } else {
      return null;
   }
}
}

Calling a method directly is 1.7 times after than use the same method.invoke. That sounds like a lot however its very quick so in the schema of all graphql processing its not a lot.

Benchmark                               Mode  Cnt         Score         Error  Units
ByteCodeGen.measureViaGeneratedClass   thrpt   15  67263591.084 ± 1577315.087  ops/s
ByteCodeGen.measureViaReflectedMethod  thrpt   15  39037523.623 ±  975625.383  ops/s

However we do get some improvements in object heavy benchmarks like the introspection one


Benchmark                                      Mode  Cnt  Score   Error  Units
IntrospectionBenchmark.benchMarkIntrospection  avgt   15  0.668 ± 0.069   s/op

vs master

Benchmark                                      Mode  Cnt  Score   Error  Units
IntrospectionBenchmark.benchMarkIntrospection  avgt   15  0.708 ± 0.078   s/op

This needs a few things to be ready.

We would need to shade Java assist - we dont want to expose this out to others.

We need a JVM wide switch to turn this on or off. Like the graphql.schema.PropertyDataFetcherHelper#setUseSetAccessible flag we have today.

I think we might turn if off in a release and let people opt in and then maybe invert that on another release.

bbakerman avatar Oct 12 '22 11:10 bbakerman

I wonder if using LambdaMetafactory would provide similar speedup vs. Method reflection but with vastly simpler implementation. Some blogs & benchmarks indicate that it's almost as fast as direct access:

  • https://wttech.blog/blog/2020/method-handles-and-lambda-metafactory/
  • https://www.optaplanner.org/blog/2018/01/09/JavaReflectionButMuchFaster.html

arlampin avatar Oct 12 '22 19:10 arlampin

I am using reflectasm for our internal graphql implementation https://github.com/EsotericSoftware/reflectasm

I think you can try this one too

He-Pin avatar Oct 13 '22 00:10 He-Pin

FWIW, In my expirience, both Javassist and Reflectasm have problems with JDK 17+ support. The only known to me code-generation library usable on newest JVMs is ByteBuddy (https://github.com/raphw/byte-buddy). Both Hibernate and Spring moved to using ByteBuddy, so it's a safe choice.

lqc avatar Oct 13 '22 07:10 lqc

@arlampin

See https://github.com/graphql-java/graphql-java/pull/2985

bbakerman avatar Oct 13 '22 09:10 bbakerman

Can we close that @bbakerman ?

andimarek avatar Oct 21 '22 00:10 andimarek

We wont be using byte code generation - its price is too high for the pay off

bbakerman avatar Oct 24 '22 09:10 bbakerman