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

Miniboxing Library - Scala.world

Vlad Ureche
September 13, 2016

Miniboxing Library - Scala.world

Miniboxing Library - Scala.world

Vlad Ureche

September 13, 2016
Tweet

More Decks by Vlad Ureche

Other Decks in Programming

Transcript

  1. scala-miniboxing.org @miniboxing The Story of a Parallel Scala Library The

    constructs used by miniboxing to optimize your code 12th of September 2016 scala.world Penrith, UK
  2. scala-miniboxing.org @miniboxing Miniboxing Miniboxing • Compiler transformation • Improves performance

    of generics – When instantiated by primitive types • Introduces optimized constructs – Automatically – Or with programmer help
  3. scala-miniboxing.org @miniboxing Miniboxing Miniboxing • Compiler transformation • Improves performance

    of generics – When instantiated by primitive types • Introduces optimized constructs – Automatically – Or with programmer help
  4. scala-miniboxing.org @miniboxing Motivation Motivation • We build cool new constructs

    and paradigms for { conn getConnection \/> "Could not connect" ← ids conn.get("id") \/> "Coult not get ids" ← } yield user
  5. scala-miniboxing.org @miniboxing Motivation Motivation • We build cool new constructs

    and paradigms • But some constructs are deeply integrated • Functions • Tuples • Arrays • Ordering • Numeric for { conn getConnection \/> "Could not connect" ← ids conn.get("id") \/> "Coult not get ids" ← } yield user
  6. scala-miniboxing.org @miniboxing Motivation Motivation val x = (x: Int) =>

    x Function syntax is sugar for anonymous classes extending FunctionX traits (or for Java8 lambdas)
  7. scala-miniboxing.org @miniboxing Motivation Motivation val x = (x: Int) =>

    x Extending Function1 behavior? Sure, add an implicit. Function syntax is sugar for anonymous classes extending FunctionX traits (or for Java8 lambdas)
  8. scala-miniboxing.org @miniboxing Motivation Motivation val x = (x: Int) =>

    x Extending Function1 behavior? Sure, add an implicit. Changing behavior (e.g. PartialFunction)? This is what we'll talk about today. Function syntax is sugar for anonymous classes extending FunctionX traits (or for Java8 lambdas)
  9. scala-miniboxing.org @miniboxing Extending vs Changing Behavior Extending vs Changing Behavior

    Adding a new method to the trait/class Making it conform to an interface
  10. scala-miniboxing.org @miniboxing Extending vs Changing Behavior Extending vs Changing Behavior

    Adding a new method to the trait/class Making it conform to an interface Changing a method's signature
  11. scala-miniboxing.org @miniboxing Extending vs Changing Behavior Extending vs Changing Behavior

    Adding a new method to the trait/class Making it conform to an interface Changing a method's signature Updating the body of a method
  12. scala-miniboxing.org @miniboxing Extending vs Changing Behavior Extending vs Changing Behavior

    Adding a new method to the trait/class Making it conform to an interface Changing a method's signature Updating the body of a method Changing the fields of a class
  13. scala-miniboxing.org @miniboxing Extending vs Changing Behavior Extending vs Changing Behavior

    Adding a new method to the trait/class Making it conform to an interface Changing a method's signature Updating the body of a method Changing the fields of a class Extending behavior Changing behavior
  14. scala-miniboxing.org @miniboxing Extending vs Changing Behavior Extending vs Changing Behavior

    Why would you need that? Adding a new method to the trait/class Making it conform to an interface Changing a method's signature Updating the body of a method Changing the fields of a class Extending behavior Changing behavior
  15. scala-miniboxing.org @miniboxing Erased Generics Erased Generics java.lang.Integer.valueOf(5) boxing is not

    great ... boxing is not great ... inflates heap requirements produces garbage value is accessed indirectly (slow) breaks locality guarantees
  16. scala-miniboxing.org @miniboxing Specialization Specialization def identity[T](t: T): T = t

    def identity(t: Object): Object = t specialization def identity_Z(t: bool): bool = t
  17. scala-miniboxing.org @miniboxing Specialization Specialization def identity[T](t: T): T = t

    def identity(t: Object): Object = t specialization def identity_Z(t: bool): bool = t def identity_C(t: char): char = t
  18. scala-miniboxing.org @miniboxing Specialization Specialization def identity[T](t: T): T = t

    def identity(t: Object): Object = t specialization def identity_Z(t: bool): bool = t def identity_C(t: char): char = t … (7 other variants)
  19. scala-miniboxing.org @miniboxing The idea behind The idea behind ... LONG

    DOUBLE INT FLOAT SHORT ... LONG DOUBLE INT FLOAT SHORT a long integer miniboxing miniboxing
  20. scala-miniboxing.org @miniboxing Miniboxing Miniboxing def identity[T](t: T): T = t

    def identity(t: Object): Object = t miniboxing def identity_M(..., t: long): long = t
  21. scala-miniboxing.org @miniboxing Miniboxing Miniboxing def identity[T](t: T): T = t

    def identity(t: Object): Object = t miniboxing def identity_M(..., t: long): long = t long encodes all primitive types
  22. scala-miniboxing.org @miniboxing Miniboxing Miniboxing def identity[T](t: T): T = t

    def identity(t: Object): Object = t miniboxing def identity_M(..., t: long): long = t Only 2n variants long encodes all primitive types
  23. scala-miniboxing.org @miniboxing Call Graph Call Graph def foo[T](t: T): T

    = bar(t) def bar[T](t: T): T = baz(t) def baz[T](t: T): T = t call graph
  24. scala-miniboxing.org @miniboxing Miniboxed Call Graph Miniboxed Call Graph def foo[@miniboxed

    T](t: T): T = bar(t) def bar[@miniboxed T](t: T): T = baz(t) def baz[@miniboxed T](t: T): T = t miniboxing
  25. scala-miniboxing.org @miniboxing Miniboxed Call Graph Miniboxed Call Graph def foo(...):

    ... = bar(...) def foo_M(...): ... = bar_M(...) def bar(...): ... = baz(...) def bar_M(...): ... = baz_M(...) def baz(...): ... = t def baz_M(...): ... = t
  26. scala-miniboxing.org @miniboxing Miniboxed Call Graph Miniboxed Call Graph foo foo_M

    bar bar_M baz baz_M Data is passed in the miniboxed encoding no boxing necessary!
  27. scala-miniboxing.org @miniboxing Suboptimal Call Graph Suboptimal Call Graph def foo[@miniboxed

    T](t: T): T = bar(t) def bar[@miniboxed T](t: T): T = baz(t) def baz[@miniboxed T](t: T): T = t miniboxing
  28. scala-miniboxing.org @miniboxing Suboptimal Call Graph Suboptimal Call Graph foo foo_M

    bar baz baz_M no bar_M to call box → not enough info to return to the optimized trace
  29. scala-miniboxing.org @miniboxing Suboptimal Call Graph Suboptimal Call Graph scala> object

    Test { | def foo[@miniboxed T](t: T): T = bar(t) | def bar[ T](t: T): T = baz(t) | def baz[@miniboxed T](t: T): T = t | } <console>:10: warning: The method Test.bar would benefit from miniboxing type parameter T, since it is instantiated by miniboxed type parameter T of method foo: def foo[@miniboxed T](t: T): T = bar(t) ^ <console>:11: warning: The following code could benefit from miniboxing specialization if the type parameter T of method bar would be marked as "@miniboxed T": def bar[T](t: T): T = baz(t) ^ defined object Test
  30. scala-miniboxing.org @miniboxing Library Interoperation Library Interoperation scala> def identityFun[T] =

    (t: T) => t defined method identityFun scala> identityFun(3) <console>:11: warning: The method identityFun would benefit from miniboxing type parameter T, since it is instantiated by a primitive type. identityFun(3) ^ res1: Int = 3
  31. scala-miniboxing.org @miniboxing scala> def identityFun[@miniboxed T] = (t: T) =>

    t <console>:12: warning: The type parameter T1 of trait scala.Function1 is specialized but to interoperate efficiently with miniboxing, it would have to be miniboxed. def identityFun[@miniboxed T] = (t: T) => t ^ <console>:12: warning: The type parameter R of trait scala.Function1 is specialized but to interoperate efficiently with miniboxing, it would have to be miniboxed. def identityFun[@miniboxed T] = (t: T) => t ^ defined method identityFun Library Interoperation Library Interoperation
  32. scala-miniboxing.org @miniboxing scala> def identityFun[@miniboxed T] = (t: T) =>

    t <console>:12: warning: The type parameter T1 of trait scala.Function1 is specialized but to interoperate efficiently with miniboxing, it would have to be miniboxed. def identityFun[@miniboxed T] = (t: T) => t ^ <console>:12: warning: The type parameter R of trait scala.Function1 is specialized but to interoperate efficiently with miniboxing, it would have to be miniboxed. def identityFun[@miniboxed T] = (t: T) => t ^ defined method identityFun Library Interoperation Library Interoperation Not actionable
  33. scala-miniboxing.org @miniboxing scala> def identityFun[@miniboxed T] = (t: T) =>

    t <console>:12: warning: The type parameter T1 of trait scala.Function1 is specialized but to interoperate efficiently with miniboxing, it would have to be miniboxed. def identityFun[@miniboxed T] = (t: T) => t ^ <console>:12: warning: The type parameter R of trait scala.Function1 is specialized but to interoperate efficiently with miniboxing, it would have to be miniboxed. def identityFun[@miniboxed T] = (t: T) => t ^ defined method identityFun Library Interoperation Library Interoperation Not actionable Need to fix it
  34. scala-miniboxing.org @miniboxing scala> def identityFun[@miniboxed T] = (t: T) =>

    t <console>:12: warning: The type parameter T1 of trait scala.Function1 is specialized but to interoperate efficiently with miniboxing, it would have to be miniboxed. def identityFun[@miniboxed T] = (t: T) => t ^ <console>:12: warning: The type parameter R of trait scala.Function1 is specialized but to interoperate efficiently with miniboxing, it would have to be miniboxed. def identityFun[@miniboxed T] = (t: T) => t ^ defined method identityFun Library Interoperation Library Interoperation Not actionable Need to fix it
  35. scala-miniboxing.org @miniboxing Library Interoperation Library Interoperation • Multiple such constructs

    – Functions automatic, most interesting → – Arrays need user intervention, warnings → – Tuples automatic → – Numeric automatic, but I was lazy →
  36. scala-miniboxing.org @miniboxing MbFunctionX MbFunctionX • Miniboxed replacement for FunctionX •

    Assumptions – Function created once – Called multiple times optimize for calling → overhead
  37. scala-miniboxing.org @miniboxing MbFunctionX MbFunctionX • Miniboxed replacement for FunctionX •

    Assumptions – Function created once – Called multiple times optimize for calling → overhead Let's see how we transform
  38. scala-miniboxing.org @miniboxing MbFunctionX MbFunctionX • Three approaches – trait Function0[@miniboxed

    @specialized R] • Incompatible transformations, won't fly – Call-site adaptation (implicit + value class) • call overhead – Replace FunctionX body • no call overhead
  39. scala-miniboxing.org @miniboxing Library Interoperation Library Interoperation scala> def identityFun[@miniboxed T]

    = (t: T) => t defined method identityFun def identityFun[@miniboxed T]: MbFunction1[T, T] = ...
  40. scala-miniboxing.org @miniboxing Library Interoperation Library Interoperation scala> def identityFun[@miniboxed T]

    = (t: T) => t defined method identityFun def identityFun[@miniboxed T]: MbFunction1[T, T] = ... Once this is done, miniboxing does its magic
  41. scala-miniboxing.org @miniboxing Library Interoperation Library Interoperation scala> def identityFun[@miniboxed T]

    = (t: T) => t defined method identityFun def identityFun[@miniboxed T]: MbFunction1[T, T] = ... Once this is done, miniboxing does its magic def identityFun[T]: MbFunction1[T, T] = { class $anon extends MbFunction1_L[T, T] { def apply(t: T) = t } new $anon() } def identityFun_M[Tsp](...): MbFunction1[T, T] = { class $anon extends MbFunction1_M[T, T](...) { def apply_M(..., t: T) = t } new $anon() }
  42. scala-miniboxing.org @miniboxing Library Interoperation Library Interoperation • Multiple such constructs

    – Functions automatic, most interesting → – Arrays need user intervention, warnings → – Tuples automatic → – Numeric automatic, but I was lazy →
  43. scala-miniboxing.org @miniboxing Extending vs Changing Behavior Extending vs Changing Behavior

    Adding a new method to the trait/class Making it conform to an interface Changing a method's signature Updating the body of a method Changing the fields of a class Extending behavior Changing behavior
  44. scala-miniboxing.org @miniboxing Extending vs Changing Behavior Extending vs Changing Behavior

    Adding a new method to the trait/class Making it conform to an interface Changing a method's signature Updating the body of a method Changing the fields of a class Extending behavior Changing behavior Completely replacing a library class
  45. scala-miniboxing.org @miniboxing Dependencies Dependencies Your library Scala stdlib Other library

    Other library dependencies Each library is free to use its desired FunctionX version
  46. scala-miniboxing.org @miniboxing Dependencies Dependencies Your library Scala stdlib Other library

    Other library dependencies Each library is free to use its desired FunctionX version Talk to each in its own "language"
  47. scala-miniboxing.org @miniboxing Dependencies Dependencies Your library Scala stdlib Other library

    Other library dependencies Each library is free to use its desired FunctionX version Talk to each in its own "language" Have to track of the transformations
  48. scala-miniboxing.org @miniboxing Marking Changes Marking Changes Source Signatures Bytecode Source

    Signatures Bytecode Source Signatures Bytecode JVM scalac scalac scalac
  49. scala-miniboxing.org @miniboxing Marking Changes Marking Changes Source Signatures Bytecode Source

    Signatures Bytecode Source Signatures Bytecode JVM scalac scalac scalac
  50. scala-miniboxing.org @miniboxing Marking Changes Marking Changes Source Signatures Bytecode Source

    Signatures Bytecode Source Signatures Bytecode JVM scalac scalac scalac
  51. scala-miniboxing.org @miniboxing Marking Changes Marking Changes • Keep track of

    changes to – Methods – Fields – Classes – Traits • Carry transformation info with types – Automatically annotate the code – The pickler phase stores them in signatures – Introduce conversions where necessary
  52. scala-miniboxing.org @miniboxing Marking Changes Marking Changes def identityFun[@miniboxed T] =

    (t: T) => t def identityFun[@miniboxed T]: Function1[T, T] = (t: T) => t typer interop-inject
  53. scala-miniboxing.org @miniboxing Marking Changes Marking Changes def identityFun[@miniboxed T] =

    (t: T) => t def identityFun[@miniboxed T]: Function1[T, T] = (t: T) => t typer interop-inject @... def identityFun[@miniboxed T]: Function1[T, T] @mbFun = (t: T) => t pickler
  54. scala-miniboxing.org @miniboxing Marking Changes Marking Changes def identityFun[@miniboxed T] =

    (t: T) => t def identityFun[@miniboxed T]: Function1[T, T] = (t: T) => t typer interop-inject @... def identityFun[@miniboxed T]: Function1[T, T] @mbFun = (t: T) => t Future compilation runs are aware of the change pickler
  55. scala-miniboxing.org @miniboxing Conversions Conversions • What do to if –

    We have an MbFunction1 and – Callee expects Function1? • Conversions – Between the two cases – Functions defined in miniboxed code • Miniboxing-friendly and specialization-friendly – Functions defined in non-miniboxed code • Specialization-friendly • Need conversion Miniboxing friendly →
  56. scala-miniboxing.org @miniboxing Conversions Conversions def identityFun[@miniboxed T] = Predef.identity def

    identityFun[@miniboxed T]: Function1[T, T] = Predef.identity[T] // : Function1[T, T] from classpath typer interop-inject
  57. scala-miniboxing.org @miniboxing Conversions Conversions def identityFun[@miniboxed T] = Predef.identity def

    identityFun[@miniboxed T]: Function1[T, T] = Predef.identity[T] // : Function1[T, T] from classpath typer interop-inject @... def identityFun[@miniboxed T]: Function1[T, T] @mbFun = Predef.identity[T] // : Function1[T, T] no annotation interop-coerce
  58. scala-miniboxing.org @miniboxing @... def identityFun[@miniboxed T]: Function1[T, T] @mbFun =

    toMbFun(Predef.identity[T]) // : Function1[T, T] @mbFun Conversions Conversions def identityFun[@miniboxed T] = Predef.identity def identityFun[@miniboxed T]: Function1[T, T] = Predef.identity[T] // : Function1[T, T] from classpath typer interop-inject @... def identityFun[@miniboxed T]: Function1[T, T] @mbFun = Predef.identity[T] // : Function1[T, T] no annotation interop-coerce
  59. scala-miniboxing.org @miniboxing Reverse Dependencies Reverse Dependencies Your library Scala stdlib

    Other library Other library Other lib 1 Other lib 2 Other lib 3 dependencies reverse dependencies
  60. scala-miniboxing.org @miniboxing Reverse Dependencies Reverse Dependencies Your library Scala stdlib

    Other library Other library Other lib 1 Other lib 2 Other lib 3 dependencies reverse dependencies
  61. scala-miniboxing.org @miniboxing Reverse Dependencies Reverse Dependencies Your library Scala stdlib

    Other library Other library Other lib 1 Other lib 2 Other lib 3 dependencies reverse dependencies Libraries not compiled with the miniboxing plugin are not aware of the transformation → incorrect bytecode
  62. scala-miniboxing.org @miniboxing Reverse Dependencies Reverse Dependencies $ scala Welcome to

    Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_101). Type in expressions to have them evaluated. Type :help for more information. scala> Test.identityFun(2) <console>:11: error: This method was compiled with the miniboxing plugin and can only be called by code also compiled with the miniboxing plugin (see scala-miniboxing.org/compatibility) [mbox=0.4-M8,scala=2.11.7] Test.identityFun(2) ^
  63. scala-miniboxing.org @miniboxing Reverse Dependencies Reverse Dependencies def identityFun[@miniboxed T] =

    (t: T) => t def identityFun[@miniboxed T]: Function1[T, T] = (t: T) => t typer interop-inject @compileTimeOnly(...) def identityFun[@miniboxed T]: Function1[T, T] @mbFun = (t: T) => t pickler
  64. scala-miniboxing.org @miniboxing Reverse Dependencies Reverse Dependencies def identityFun[@miniboxed T] =

    (t: T) => t def identityFun[@miniboxed T]: Function1[T, T] = (t: T) => t typer interop-inject @compileTimeOnly(...) def identityFun[@miniboxed T]: Function1[T, T] @mbFun = (t: T) => t Built into the Scala stdlib and recognized by scalac (checked by refchecks) pickler
  65. scala-miniboxing.org @miniboxing Object Model Object Model trait IdentityFun { def

    identityFun[T]: T => T // = Function1 [T, T] } object MbIdentityFun extends IdentityFun { def identityFun[@miniboxed T]: T => T = (t: T) => t }
  66. scala-miniboxing.org @miniboxing Object Model Object Model trait IdentityFun { def

    identityFun[T]: T => T // = Function1 [T, T] } object MbIdentityFun extends IdentityFun { def identityFun[@miniboxed T]: T => T = (t: T) => t } scala> MbIdentityFun.identityFun(2) <console>:11: error: ... scala> val id: IdentityFun = MbIdentityFun id: IdentityFun = MbIdentityFun$@23223dd8 scala> id.identityFun(3) res1: Int = 3
  67. scala-miniboxing.org @miniboxing Object Model Object Model trait IdentityFun { def

    identityFun[T]: T => T // = Function1 [T, T] } object MbIdentityFun extends IdentityFun { def identityFun[@miniboxed T]: T => T = (t: T) => t } scala> MbIdentityFun.identityFun(2) <console>:11: error: ... scala> val id: IdentityFun = MbIdentityFun id: IdentityFun = MbIdentityFun$@23223dd8 scala> id.identityFun(3) res1: Int = 3 You can "hide" miniboxed code behind an interface
  68. scala-miniboxing.org @miniboxing Object Model Object Model • Allows "hiding" miniboxed

    code – Under an erased interface (example) – Under a specialized interface • Pro: no dependency on the plugin downstream • Cons: have to write some glue code
  69. scala-miniboxing.org @miniboxing RRBVector Experiment RRBVector Experiment • RRBVector is an

    immutable vector data structure – With constant-time split and merge (for parallel ops) • Idea by Phil Bagwell and Tiark Rompf • Implementation by Nicolas Stucki – 4 developer-weeks of work – Hand-tuned for maximum performance – Scaling almost linearly with the number of cores – Summing a vector is 2x as slow as summing an array • Presented at ICFP '15
  70. scala-miniboxing.org @miniboxing RRBVector Experiment RRBVector Experiment • Questions: – How

    difficult is it to specialize RRBVector with miniboxing? – What are the speedups obtained by miniboxing? • Preparation: – Copied the Scala stdlib suppor necessary for RRBVector (3k LOC) – Eliminated the parallel execution support (3k 2k LOC) → – Prepared benchmarks • Subject: Milos Stojanovic – Master student working in the lab – Experienced with miniboxing – Never saw the RRBVector code base before
  71. scala-miniboxing.org @miniboxing RRBVector Experiment RRBVector Experiment • It took 30

    minutes to optimize the code base by following the miniboxing warnings → 0.3% of the development time Benchmark Microbenchmarks (5M elements) Macrobenchmark Builder map fold reverse* Least squares linear regression (5M points) Erased (seconds) 43.2 103.0 94.1 31.4 4864.6 Miniboxed (seconds) 22.9 60.4 42.1 35.3 1818.1 Speedup (ratio) 1.88x 1.71x 2.24x 0.89x 2.68x * reversing a vector does not perform any operations that could benefit from miniboxing.
  72. scala-miniboxing.org @miniboxing RRBVector Experiment RRBVector Experiment • Used library features

    – MbFunction – MbArray – Tuples – Numeric – Ordering Impossible to cover today
  73. scala-miniboxing.org @miniboxing Miniboxed Library Miniboxed Library • Replaces deeply integrated

    Scala constructs – Preserve the syntactic sugar – No source code changes • Scalac/miniboxing does a lot of the work • Use cases – Miniboxing: performance – <your use case here>