Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TruffleReloader: A Low-Overhead Language-Neutra...

TruffleReloader: A Low-Overhead Language-Neutral Reloader

Avatar for ICOOOLPS Workshop

ICOOOLPS Workshop

July 18, 2016
Tweet

More Decks by ICOOOLPS Workshop

Other Decks in Science

Transcript

  1. > Reloading Java via bytecode rewriting > 100+ integrations >

    Application servers > Frameworks > IDEs
  2. Truffle > Oracle Labs & Johannes Kepler University Linz >

    Reusing JVM runtime > Java API for AST interpreters > Language context > C, JavaScript, Ruby, Python, R, LLVM IR
  3. def add(first, second): return first + second add(1, 1) U

    U U 1. read first 2. read second 3. add
  4. U U I 1. read first 2. read second 3.

    add U I I 1. read first 2. read second 3. add
  5. I I L 1. read first 2. read second 3.

    add I I I 1. read first 2. read second 3. add
  6. > Assume the tree is stable and compile AST (sub)tree

    to machine code > Deoptimize when assumptions invalidated
  7. Truffle Instrumentation API > Access to the execution state of

    Truffle interpreters > Wrapper nodes > Delegate to wrapped nodes > Fire events
  8. TruffleReloader > Reusable reloading core > Low overhead > Java

    agent > -javaagent:/path/to/truffle-reloader.jar
  9. Requirements for reloading 1. Detect source code changes 2. Source

    code parsing 3. Updating the execution tree
  10. public interface LanguageReloader { String mimeType(); Supplier<CallTargetIdentity> getIdentityFor( RootCallTarget callTarget);

    ReplayController getReplayController(); default Predicate<String> acceptCodePath() { return (path) -> true; } }
  11. public interface LanguageReloader { String mimeType(); Supplier<CallTargetIdentity> getIdentityFor( RootCallTarget callTarget);

    ReplayController getReplayController(); default Predicate<String> acceptCodePath() { return (path) -> true; } }
  12. public interface LanguageReloader { String mimeType(); Supplier<CallTargetIdentity> getIdentityFor( RootCallTarget callTarget);

    ReplayController getReplayController(); default Predicate<String> acceptCodePath() { return (path) -> true; } }
  13. public interface LanguageReloader { String mimeType(); Supplier<CallTargetIdentity> getIdentityFor( RootCallTarget callTarget);

    ReplayController getReplayController(); default Predicate<String> acceptCodePath() { return (path) -> true; } }
  14. public interface LanguageReloader { String mimeType(); Supplier<CallTargetIdentity> getIdentityFor( RootCallTarget callTarget);

    ReplayController getReplayController(); default Predicate<String> acceptCodePath() { return (path) -> true; } }
  15. public interface ReplayController<T extends ExecutionContext> { default void beforeStart(T context,

    Source source, Object[] currentArgs) {} default void afterStop(T context, Source source, Object[] currentArgs) {} boolean shouldStopAt( RootCallTarget executableCallTarget); }
  16. public interface ReplayController<T extends ExecutionContext> { default void beforeStart(T context,

    Source source, Object[] currentArgs) {} default void afterStop(T context, Source source, Object[] currentArgs) {} boolean shouldStopAt( RootCallTarget executableCallTarget); }
  17. public interface ReplayController<T extends ExecutionContext> { default void beforeStart(T context,

    Source source, Object[] currentArgs) {} default void afterStop(T context, Source source, Object[] currentArgs) {} boolean shouldStopAt( RootCallTarget executableCallTarget); }
  18. Proxy CallNodes 1. Has the Source changed? 2. Are we

    running Partial AST Replay? Guard the actions with Assumptions
  19. public class SourceFileChecker { private static void run() { while

    (true) { wakeUp.await(CHECK_FREQUENCY, MILLISECONDS); sourceFiles.forEach((file, reloader) -> { long currentModified = file.lastModified(); if (map.get(file) != currentModified) { map.put(file, currentModified); reloader.invalidate(); } }); } } }
  20. public class SourceFileChecker { private static void run() { while

    (true) { wakeUp.await(CHECK_FREQUENCY, MILLISECONDS); sourceFiles.forEach((file, reloader) -> { long currentModified = file.lastModified(); if (map.get(file) != currentModified) { map.put(file, currentModified); reloader.invalidate(); } }); } } }
  21. public class SourceFileChecker { private static void run() { while

    (true) { wakeUp.await(CHECK_FREQUENCY, MILLISECONDS); sourceFiles.forEach((file, reloader) -> { long currentModified = file.lastModified(); if (map.get(file) != currentModified) { map.put(file, currentModified); reloader.invalidate(); } }); } } }
  22. Evaluation > Graal.js, ZipPy, JRuby+Truffle and Simple Language > Functionality

    > Performance > https://github.com/poolik/bench9000 > Case Study
  23. Use case SL ZipPy JRuby+Truffle Graal.js Changing a function y

    y y y Changing multiple functions y y y y Adding a new function y y y y Multiple consecutive reloads y y y y Changing global variable definition n/a y y y Adding a new global variable definition n/a y y y Reloading functions with same name n/a y y y Adding a new class n/a y y y Changing a static method n/a y y y Changing an instance method n/a y y y Adding a new instance method used from variable n/a y y y Adding a new instance method used from new object n/a y y y Adding a new instance method used from field n/a y y y Adding a new instance method used from global variable n/a y y y Multiple file reload, changing a method in dependency n/a y y n/a Multiple file reload, adding a new method in dependency n/a y y n/a Multiple file reload, changing an instance method in dependency on existing object n/a y y n/a Multiple file reload, changing an instance method in dependency on new object n/a y y n/a
  24. Case Study > Simple OSS Ruby web application > Fixed

    bugs & added features > https://github.com/poolik/sinatra_todo
  25. TruffleReloader > Reusable reloading core > Ruby LanguageReloader ~ 80

    LOC > Python LanguageReloader ~ 170 LOC > JS LanguageReloader ~ 160 LOC > Low overhead