Doom, gloom or loom

Doom, gloom or loom

RivieraDEV presentation of OpenJDK project loom

5ce0156bc2f1864459fd77229eff3fe3?s=128

forax

May 16, 2019
Tweet

Transcript

  1. Doom, gloom or loom RivieraDev 2019 Rémi Forax

  2. Me, Myself and I Java Champion, OpenJDK amber & valhalla

    ex: jigsaw, lambda, invokedynamic
  3. Imperative vs Reactive ?? Imperative with Spring 5: @GetMapping("/tweets/{id}") public

    ResponseEntity<Tweet> getTweetById(@PathVariable("id") String tweetId) { return tweetRepository.findById(tweetId) // Optional<...> .map(ResponseEntity::ok) .orElseGet(ResponseEntity::notFound); }
  4. Imperative vs Reactive ?? Imperative with Spring 5: @GetMapping("/tweets/{id}") public

    ResponseEntity<Tweet> getTweetById(@PathVariable("id") String tweetId) { return tweetRepository.findById(tweetId) // Optional<...> .map(ResponseEntity::ok) .orElseGet(ResponseEntity::notFound); } Reactive with Spring 5: @GetMapping("/tweets/{id}") public Mono<ResponseEntity<Tweet>> getTweetById(@PathVariable("id") String tweetId) { return tweetRepository.findById(tweetId) // Mono<...> .map(ResponseEntity::ok) .defaultIfEmpty(ResponseEntity.notFound()); }
  5. Continuation OpenJDK Project Loom

  6. Demo Continuation !

  7. A simple example var scope = new ContinuationScope("scope"); var continuation

    = new Continuation(scope, () -> { System.out.println("hello rivieradev"); Continuation.yield(scope); System.out.println("i'm back"); }); continuation.run(); System.out.println("restart"); continuation.run();
  8. A continuation A continuation wraps a Runnable/Callable – Multiple entry

    points Operations: Outside the continuation: start/restart the continuation • continuation.run() Inside the continuation: jump back to the caller • Continuation.yield(scope)
  9. But Why ??

  10. Thread are heavyweight ! A Thread – Share the same

    address space – Lightweight Lock • no context switch but – Thread stack is pre-allocated (=> OOM if a lot of threads) – Thread scheduling is done by the OS !
  11. Concurrency models continuation model Memory OS VM T1 T2 T3

    lock thread model
  12. Concurrency models continuation model Memory OS VM T1 T2 T3

    lock thread model Memory OS VM T1 T2 lock C1 C2 C3
  13. It already exists in C#, Kotlin or JavaScript ??

  14. Demo async()/Deferred.await() in Kotlin

  15. async()/Deferred.await() in Kotlin • runBlocking { val deferred1 = async

    { delay(1_000) "1" } val deferred2 = async { delay(1_000) "2" } val deferreds = listOf(deferred1, deferred2) //wait for all results val results = deferreds.map { it.await() } println(results) }
  16. Suspended function suspend fun doIt(s: String): String { delay(1_000) return

    s } fun kotlinxCompositionExample() { runBlocking { val deferred1 = async { doIt("1") } val deferred2 = async { doIt("2") } val deferreds = listOf(deferred1, deferred2) ...
  17. Demo Fiber.schedule()/join() in Java

  18. Fiber.schedule/join in Java var fiber1 = Fiber.schedule(() -> { Thread.sleep(1_000);

    return "1"; }); var fiber2 = Fiber.schedule(() -> { Thread.sleep(1_000); return "2"; }); var fibers = List.of(fiber1, fiber2); //wait for all results var results = fibers.stream().map(Fiber::join).collect(toList()); System.out.println(results);
  19. No suspended keyword ! static String doIt(String s) throws InterruptedException

    { sleep(1_000); return s; } static void javaExample() { var fiber1 = schedule(() -> doIt("1")); var fiber2 = schedule(() -> doIt("2")); var fibers = List.of(fiber1, fiber2); ...
  20. Fiber Wrap a Runnable/Callable Scheduled using an ExecutorService fork/join common

    pool by default A fiber run on a thread until a blocking call
  21. Fiber / blocking calls Blocking calls (read, write, sleep) –

    Yield from the fiber, schedule another one – Re-scheduled • When a read/write/locks/etc will not block • Maybe scheduled on another thread
  22. Under the hood ...

  23. … in Kotlin ??

  24. Suspended function in bytecode ? In Kotlin, the compiler does

    the transformation suspend fun doIt(s: String): String { delay(1_000) return s }
  25. suspend doIt() in bytecode public static final java.lang.Object doIt(java.lang.String, kotlin.coroutines.Continuation<?

    super java.lang.String>); Code: 0: aload_1 1: instanceof #11 // class AsyncCompositionKt$doIt$1 4: ifeq 36 7: aload_1 8: checkcast #11 // class AsyncCompositionKt$doIt$1 11: astore_3 12: aload_3 13: getfield #15 // Field AsyncCompositionKt$doIt$1.label:I 16: ldc #16 // int -2147483648 18: iand 19: ifeq 36 22: aload_3 23: dup 24: getfield #15 // Field AsyncCompositionKt$doIt$1.label:I 27: ldc #16 // int -2147483648 29: isub 30: putfield #15 // Field AsyncCompositionKt$doIt$1.label:I 33: goto 45 36: new #11 // class AsyncCompositionKt$doIt$1 39: dup 40: aload_1 41: invokespecial #20 // Method AsyncCompositionKt$doIt$1."<init>":(Lkotlin/coroutines/Continuation;)V 44: astore_3 45: aload_3 46: getfield #24 // Field AsyncCompositionKt$doIt$1.result:Ljava/lang/Object; 49: astore_2 50: invokestatic #30 // Method kotlin/coroutines/intrinsics/IntrinsicsKt.getCOROUTINE_SUSPENDED:()Ljava/lang/Object; 53: astore 4
  26. suspend doIt() in bytecode public static final java.lang.Object doIt(java.lang.String, kotlin.coroutines.Continuation<?

    super java.lang.String>); ... 55: aload_3 56: getfield #15 // Field AsyncCompositionKt$doIt$1.label:I 59: tableswitch { // 0 to 1 0: 80 1: 110 default: 126 } 80: aload_2 81: invokestatic #36 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V 84: ldc2_w #37 // long 1000l 87: aload_3 88: aload_3 89: aload_0 90: putfield #41 // Field AsyncCompositionKt$doIt$1.L$0:Ljava/lang/Object; 93: aload_3 94: iconst_1 95: putfield #15 // Field AsyncCompositionKt$doIt$1.label:I 98: invokestatic #47 // Method kotlinx/coroutines/DelayKt.delay:(JLkotlin/coroutines/Continuation;)Ljava/lang/Object; 101: dup 102: aload 4 104: if_acmpne 123 107: aload 4 109: areturn 110: aload_3 111: getfield #41 // Field AsyncCompositionKt$doIt$1.L$0:Ljava/lang/Object; 114: checkcast #49 // class java/lang/String 117: astore_0 118: aload_2 ...
  27. suspend doIt() in Java final class AsyncCompositionKt$doIt$1 extends kotlin.coroutines.jvm.internal.ContinuationImpl {

    int label; Object L$0; ... } public static final Object doIt(String s, kotlin.coroutines.Continuation<? super String> cont) { AsyncCompositionKt$doIt$1 c; if (continuation instanceof AsyncCompositionKt$doIt$1) { c = (AsyncCompositionKt$doIt$1) cont; } else { c = new AsyncCompositionKt$doIt$1(); } switch(c.label) { case 0: c.L$0 = s; c.label = 1; DelayKt.delay(1_000, c); // <– suspended method return (String) c.L$0; case 1: return (String) c.L$0; default: throw new ISE(...) }
  28. Kotlin async/await Transformation in state machine by the compiler Store

    in fields even if the method call is not suspended – Can be improved ?? Generate a lot of bytecodes Can be improved at bit Kotlinc uses javac as back-end => lot of checkcasts
  29. … in Java ??

  30. How does it work ? Yield copy the all stack

    frames on heap ! main run f1 f2 heap run f1 f2
  31. How does it work ? Then run() move some stack

    frames back main f2 heap stack bang! The VM tries to guess how many stack frames should be moved A read of the stack bang trigger the copy of the remaining frames
  32. Delimited Continuation vs Thread Scheduling explicit (yield, run) – No

    OS context switch No heap reservation – Only store what is actually needed !
  33. Demo TCP Proxy

  34. TCP Proxy with Threads var server = ServerSocketChannel.open(); server.bind(new InetSocketAddress(7777));

    System.out.println("server bound to " + server.getLocalAddress()); var remote = SocketChannel.open(); remote.connect(new InetSocketAddress(InetAddress.getByName(Host.NAME), 7)); System.out.println("accepting ..."); var client = server.accept(); new Thread(runnable(client, remote)).start(); new Thread(runnable(remote, client)).start();
  35. TCP Proxy with Fibers var server = ServerSocketChannel.open(); server.bind(new InetSocketAddress(7777));

    System.out.println("server bound to " + server.getLocalAddress()); var remote = SocketChannel.open(); remote.connect(new InetSocketAddress(InetAddress.getByName(Host.NAME), 7)); System.out.println("accepting ..."); var client = server.accept(); var executor = Executors.newSingleThreadExecutor(); //var executor = ForkJoinPool.commonPool(); Fiber.schedule(executor, runnable(client, remote)); Fiber.schedule(executor, runnable(remote, client));
  36. Fiber vs Continuation A fiber delegates the suspension (yield) to

    the continuation – All blocking calls do internally a yield – The JDK code calls run() on the continuation when it can be rescheduled All the JDK needs to be patched to be “fiber aware”
  37. Fiber/Continuation limitation Continuation/Fiber – Can not yield if there is

    a native frame on the stack Fiber – Yielding inside a synchronized block pins the fiber to that thread • The same thread as to be used when re-scheduling • This limitation may be removed in the future
  38. Summary

  39. Kotlin vs Loom Kotlin – requires a new compiler –

    Bytecode bloat (parts can be improved) but Fast ! – Split world, “suspended” or not Java Loom – requires a new VM – No fat but not fast (yet ?) – One world
  40. Reactive Programming is Dead ? One aspect is managing asynchronous

    calls but Reactive APIs may also provides – back-pressure – auto-retry, failover, etc – data flow computation lazy computation, cycle detection, incremental updates
  41. Questions ?