Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

A Java Implementer's Guide to Boosting Apache Spark Performance by Tim Ellison.


Published on

Apache Spark has rocked the big data landscape, quickly becoming the largest open source big data community with over 750 contributors from more than 200 organizations. Spark's core tenants of speed, ease of use, and its unified programming model fit neatly with the high performance, scalable, and manageable characteristics of modern Java runtimes. In this talk we introduce the Spark programming model, and describe some unique Java runtime capabilities in the JIT, fast networking, serialization techniques, and GPU off-loading that deliver the ultimate big data platform for solving business problems. We will show how solutions, previously infeasible with regular Java programming, become possible with a high performance Spark core runtime, enabling you to solve problems smarter and faster.

Published in: Software
  • Be the first to comment

  • Be the first to like this

A Java Implementer's Guide to Boosting Apache Spark Performance by Tim Ellison.

  1. 1. © 2015 IBM Corporation A Java Implementer's Guide to Boosting Apache Spark Performance Tim Ellison IBM Runtimes Team, Hursley, UK tellison @tpellison Flickr: G B. CC BY­ND 2.0
  2. 2. © 2016 IBM Corporation IBM's Java SE technology Reference Java Technology Oracle, open source, etc IBM Runtime Technology Centre Quality Engineering Performance Security Reliability Scalability Serviceability Production Requirements IBM Software Group IBM hardware ISVs IBM Clients AWT Swing Java2d XML Crypto Corba Core libraries “J9” virtual machine  Deep integration with hardware and software innovation  Available on more platforms than any other vendor  Heuristics optimised for product and customer scenarios
  3. 3. © 2016 IBM Corporation3 Apache Spark is a fast, general purpose cluster computing platform
  4. 4. © 2016 IBM Corporation4 SQL Streaming Machine Learning Graph Core Data Frames Machine Learning Pipelines
  5. 5. © 2016 IBM Corporation5 Apache Spark APIs  Spark Core – Provides APIs for working with raw data collections – Map / reduce functions to transform and evaluate the data – Filter, aggregation, grouping, joins, sorting  Spark SQL – APIs for working with structured and semi-structured data – Loads data from a variety of sources (DB2, JSON, Parquet, etc) – Provides SQL interface to external tools (JDBC/ODBC)  Spark Streaming – Discretized streams of data arriving over time – Fault tolerant and long running tasks – Integrates with batch processing of data  Machine Learning (MLlib) – Efficient, iterative algorithms across distributed datasets – Focus on parallel algorithms that run well on clusters – Relatively low-level (e.g. K-means, alternating least squares)  Graph Computation (GraphX) – View the same data as graph or collection-based – Transform and join graphs to manipulate data sets – PageRank, Label propagation, strongly connected, triangle count, ...
  6. 6. © 2016 IBM Corporation6 Cluster Computing Platform  Master Node “the driver” Evaluates user operations – Creates a physical execution plan to obtain the final result (a “job”) – Works backwards to determine what individual “tasks” are required to produce the answer – Optimizes the required tasks using pipelining for parallelizable tasks, reusing intermediate results, including persisting temporary states, etc (“stages of the job”) – Distributes work out to worker nodes – Tracks the location of data and tasks – Deals with errant workers  Worker Nodes “the executors” in a cluster Executes tasks – Receives a copy of the application code – Receives data, or the location of data partitions – Performs the required operation – Writes output to another input, or storage driver job job job executor task task task executor task task task
  7. 7. © 2016 IBM Corporation7 Resilient Distributed Dataset  The Resilient Distributed Dataset (RDD) is the target of program operations  Conceptually, one large collection of all your data elements – can be huge!  Can be the original input data, or intermediate results from other operations  In the Apache Spark implementation, RDDs are: – Further decomposed into partitions – Persisted in memory or on disk – Fault tolerant – Lazily evaluated – Have a concept of location optimization RDD1 derived from partitions RDD1 partition1 RDD1 partition 2 RDD1 partition 1 RDD1 partition 3 RDD1 partition n... f(x) partitioner + preferred location
  8. 8. © 2016 IBM Corporation8 Performance of the Apache Spark Runtime Core  Moving data blocks – How quickly can a worker get the data needed for this task? – How quickly can a worker persist the results if required?  Executing tasks – How quickly can a worker sort, compute, transform, … the data in this partition? – Can a fast worker work-steal or run speculative tasks? “Narrow” RDD dependencies e.g. map() pipeline-able “Wide” RDD dependencies e.g. reduce() shuffles RDD1 partition1 RDD1 partition 2 RDD1 partition 1 RDD1 partition 3 RDD1 partition n... RDD1 partition1 RDD2 partition 2 RDD2 partition 1 RDD2 partition 3 RDD2 partition n... RDD1 partition1 RDD3 partition 2 RDD3 partition 1 RDD3 partition 3 RDD3 partition n... RDD1 partition1 RDD1 partition 2 RDD1 partition 1 RDD1 partition 3 RDD1 partition n... RDD1 partition1 RDD2 partition 2 RDD2 partition 1
  9. 9. © 2016 IBM Corporation9 A few things we can do with the JVM to enhance the performance of Apache Spark! 1) JIT compiler enhancements, and writing JIT-friendly code 2) Improving the object serializer 3) Faster IO – networking and storage 4) Offloading tasks to graphics co-processors (GPUs)
  10. 10. © 2016 IBM Corporation10 JIT compiler enhancements, and writing JIT-friendly code
  11. 11. © 2016 IBM Corporation11 Java Adaptive Compilation : trade off effort and benefit  The VM searches the JAR, loads and verifies bytecodes to internal representation, runs bytecode form directly  After many invocations (or via sampling) code gets compiled at ‘cold’ or ‘warm’ level  An internal, low overhead sampling thread is used to identify frequently used methods  Methods may get recompiled at ‘hot’ or ‘scorching’ levels (for more optimizations)  Transition to ‘scorching’ goes through a temporary profiling step cold hot scorching profiling interpreter warm Java's intermediate bytecodes are compiled as required and based on runtime profiling - code is compiled 'just in time' as required - dynamic compilation can determine the target machine capabilities and app demands The JIT takes a holistic view of the application, looking for global optimizations based on actual usage patterns, and speculative assumptions.
  12. 12. © 2016 IBM Corporation12 JNI calls are not free!­java/blob/develop/src/main/java/org/xerial/snappy/SnappyNative.cpp
  13. 13. © 2016 IBM Corporation13 Style: Using JNI has an impact...  The cost of calling from Java code to natives and from natives to Java code is significantly higher (maybe 5x longer) than a normal Java method call. – The JIT can't in-line native methods. – The JIT can't do data flow analysis into JNI calls • e.g. it has to assume that all parameters are always used. – The JIT has to set up the call stack and parameters for C calling convention, • i.e. maybe rearranging items on the stack.  JNI can introduce additional data copying costs – There's no guarantee that you will get a direct pointer to the array / string with Get<type>ArrayElements(), even when using the GetPrimitiveArrayCritical versions. – The IBM JVM will always return a copy (to allow GC to continue).  Tip: – JNI natives are more expensive than plain Java calls. – e.g. create an unsafe based Snappy-like package written in Java code so that JNI cost is eliminated.
  14. 14. © 2016 IBM Corporation14 Style: Use JIT optimizations to reduce overhead of logging checks  Tip: Check for the non-null value of a static field ref to instance of a logging class singleton – e.g. – Uses the JIT's speculative optimization to avoid the explicit test for logging being enabled; instead it ... 1)Generates an internal JIT runtime assumption (e.g. InfoLogger.class is undefined), 2)NOPs the test for trace enablement 3)Uses a class initialization hook for the InfoLogger.class (already necessary for instantiating the class) 4)The JIT will regenerate the test code if the class event is fired  Apache Spark's logging calls are gated on the checks of a static boolean value trait Logging Spark
  15. 15. © 2016 IBM Corporation15 Style: Judicious use of polymorphism  Apache Spark has a number of highly polymorphic interface call sites and high fan-in (several calling contexts invoking the same callee method) in map, reduce, filter, flatMap, ... – e.g. ExternalSorter.insertAll is very hot (drains an iterator using hasNext/next calls)  Pattern #1: – InterruptibleIterator → Scala's mapIterator → Scala's filterIterator → …  Pattern #2: – InterruptibleIterator → Scala's filterIterator → Scala's mapIterator → …  The JIT can only choose one pattern to in-line! – Makes JIT devirtualization and speculation more risky; using profiling information from a different context could lead to incorrect devirtualization. – More conservative speculation, or good phase change detection and recovery are needed in the JIT compiler to avoid getting it wrong.  Lambdas and functions as arguments, by definition, introduce different code flow targets – Passing in widely implemented interfaces produce many different bytecode sequences – When we in-line we have to put runtime checks ahead of in-lined method bodies to make sure we are going to run the right method! – Often specialized classes are used only in a very limited number of places, but the majority of the code does not use these classes and pays a heavy penalty – e.g. Scala's attempt to specialize Tuple2 Int argument does more harm than good!  Tip: Use polymorphism sparingly, use the same order / patterns for nested & wrappered code, and keep call sites homogeneous.
  16. 16. © 2016 IBM Corporation16 Effect of Adjusting JIT heuristics for Apache Spark IBM JDK8 SR3 (tuned) IBM JDK8 SR3 (out of the box) PageRank 160% 148% Sleep 101% 113% Sort 103% 147% WordCount 130% 146% Bayes 100% 91% Terasort 157% 131% Geometric mean 121% 116% 1/Geometric mean of HiBench time on zLinux 32 cores, 25G heap Improvements in successive IBM Java 8 releases Performance compared with OpenJDK 8 HiBench huge, Spark 1.5.2, Linux Power8 12 core * 8-way SMT 1.35x
  17. 17. © 2016 IBM Corporation17 IBM Runtime performance ● IBM JDK is faster in all of the three Databricks performance scenarios we attempted ● 30% advantage in the 50 GB heap size scenario 30-60 GB is the sweet spot for showing large performance advantages compared to other JDK vendors
  18. 18. © 2016 IBM Corporation18 Replacing the object serializer
  19. 19. © 2016 IBM Corporation19 Writing an Apache Spark-friendly object serializer  Spark has a plug-in architecture for flattening objects to storage – Typically uses general purpose serializers, e.g. Java serializer, or Kryo, etc.  Can we optimize for Apache Spark usage? – Goal: Reduce time time to flatten objects – Goal: Reduce size of flattened objects  Expanding the list of specialist serialized form – Having custom write/read object methods allows for reduced time in reflection and smaller on-wire payloads. – Types such as Tuple and Some given special treatment in the serializer  Sharing object representation within the serialized stream to reduce payload – But may be defeated if supportsRelocationOfSerializedObjects required  Reduce the payload size further using variable length encoding of primitive types. – All objects are eventually decomposed into primitives
  20. 20. © 2016 IBM Corporation20 Writing an Apache Spark-friendly object serializer  Adaptive stack-based recursive serialization vs. state machine serialization – Use the stack to track state wherever possible, but fall back to state machine for deeply nested objects (e.g. big RDDs)  Special replacement of deserialization calls to avoid stack-walking to find class loader context – Optimization in JIT to circumvent some regular calls to more efficient versions  Tip: These are opaque to the application, no special patterns required.  Results: Variable, small numbers of percentages at best
  21. 21. © 2016 IBM Corporation21 Faster IO – networking and storage
  22. 22. © 2016 IBM Corporation Remote Direct Memory Access (RDMA) Networking Spark VM Buffer Off Heap Buffer Spark VM Buffer Off Heap Buffer Ether/IB SwitchRDMA NIC/HCA RDMA NIC/HCA OS OS DMA DMA (Z-Copy) (Z-Copy) (B-Copy)(B-Copy) Acronyms: Z-Copy – Zero Copy B-Copy – Buffer Copy IB – InfiniBand Ether - Ethernet NIC – Network Interface Card HCA – Host Control Adapter ● Low-latency, high-throughput networking ● Direct 'application to application' memory pointer exchange between remote hosts ● Off-load network processing to RDMA NIC/HCA – OS/Kernel Bypass (zero-copy) ● Introduces new IO characteristics that can influence the Spark transfer plan Spark node #1 Spark node #2
  23. 23. © 2016 IBM Corporation23 TCP/IP RDMA RDMA exhibits improved throughput and reduced latency. Available over APIs or explicit jVerbs calls Characteristics of RDMA Networking
  24. 24. © 2016 IBM Corporation Modifications for an RDMA-enabled Apache Spark 24 New dynamic transfer plan that adapts to the load and responsiveness of the remote hosts. New “RDMA” shuffle IO mode with lower latency and higher throughput. JVM-agnostic IBM JVM only JVM-agnostic IBM JVM only IBM JVM only Block manipulation (i.e., RDD partitions) High-level API JVM-agnostic working prototype with RDMA
  25. 25. © 2016 IBM Corporation TCP-H benchmark 100Gb 30% improvement in database operations Shuffle-intensive benchmarks show 30% - 40% better performance with RDMA HiBench PageRank 3Gb 40% faster, lower CPU usage 32 cores (1 master, 4 nodes x 8 cores/node, 32GB Mem/node)
  26. 26. © 2016 IBM Corporation26 Time to complete TeraSort benchmark 32 cores (1 master, 4 nodes x 8 cores/node, 32GB Mem/node), IBM Java 8 0 100 200 300 400 500 600 Spark HiBench TeraSort [30GB] Execution Time (sec) 556s 159s TCP/IP JSoR
  27. 27. © 2016 IBM Corporation27 Faster storage with POWER CAPI/Flash  POWER8 architecture offers a 40Tb Flash drive attached via Coherent Accelerator Processor Interface (CAPI) – Provides simple coherent block IO APIs – No file system overhead  Power Service Layer (PSL) – Performs Address Translations – Maintains Cache – Simple, but powerful interface to the Accelerator unit  Coherent Accelerator Processor Proxy (CAPP) – Maintains directory of cache lines held by Accelerator – Snoops PowerBus on behalf of Accelerator 
  28. 28. © 2016 IBM Corporation28 Faster disk IO with CAPI/Flash-enabled Spark  When under memory pressure, Spark spills RDDs to disk. – Happens in ExternalAppendOnlyMap and ExternalSorter  We have modified Spark to spill to the high-bandwidth, coherently-attached Flash device instead. – Replacement for DiskBlockManager – New FlashBlockManager handles spill to/from flash  Making this pluggable requires some further abstraction in Spark: – Spill code assumes using disks, and depends on DiskBlockManger – We are spilling without using a file system layer  Dramatically improves performance of executors under memory pressure.  Allows to reach similar performance with much less memory (denser deployments). IBM Flash System 840Power8 + CAPI
  29. 29. © 2016 IBM Corporation29 e.g. using CAPI Flash for RDD caching allows for 4X memory reduction while maintaining equal performance
  30. 30. © 2016 IBM Corporation30 Offloading tasks to graphics co-processors
  31. 31. © 2016 IBM Corporation31 GPU-enabled array sort method IBM Power 8 with Nvidia K40m GPU  Some Arrays.sort() methods will offload work to GPUs today – e.g. sorting large arrays of ints
  32. 32. © 2016 IBM Corporation32 JIT optimized GPU acceleration  Comes with caveats – Recognize a limited set of operations within the lambda expressions, • notably no object references maintained on GPU – Default grid dimensions and operating parameters for the GPU workload – Redundant/pessimistic data transfer between host and device • Not using GPU shared memory – Limited heuristics about when to invoke the GPU and when to generate CPU instructions  As the JIT compiles a stream expression we can identify candidates for GPU off-loading – Arrays copied to and from the device implicitly – Java operations mapped to GPU kernel operations – Preserves the standard Java syntax and semantics bytecodes intermediate representation optimizer CPU GPU code generator code generator PTX ISACPU native
  33. 33. © 2016 IBM Corporation33 GPU optimization of Lambda expressions Speed-up factor when run on a GPU enabled host IBM Power 8 with Nvidia K40m GPU 0.00 0.01 0.10 1.00 10.00 100.00 1000.00 auto-SIMD parallel forEach on CPU parallel forEach on GPU matrix size The JIT can recognize parallel stream code, and automatically compile down to the GPU.
  34. 34. © 2016 IBM Corporation Learn Predict Moving high-level algorithms onto the GPU Drug1 Drug2 Aspirin Gliclazide Aspirin Dicoumarol Drug1 Drug2 Sim Salsalate Aspirin .9 Dicoumarol Warfarin .76 Known Interactions of type 1 to … Drug1 Drug2 Best Sim1*Sim1 Best SimN*SimN Salsalate Gliclazid e .9*1 .7*1 Salsalate Warfarin .9*.76 .7*.6 Chemical Similarity Drug1 Drug2 Prediction Salsalate Gliclazide 0.85 Salsalate Warfarin 0.7 … Drug1 Drug2 Prediction Salsalate Gliclazide 0.53 Salsalate Warfarin 0.32 Logistic Regression ModelDrug1 Drug2 Sim Salsalate Aspirin .7 Dicoumarol Warfarin .6 Interactions Ingest Drug1 Drug2 Aspirin Probenecid Aspirin Azilsartan Interactions Prediction
  35. 35. © 2016 IBM Corporation • 25X Speed up for Building Model stage (replacing Spark Mllib Logistic Regression) • Transparent to the Spark application, but requires changes to Spark itself
  36. 36. © 2016 IBM Corporation36 Summary  We are focused on Core runtime performance to get a multiplier up the Spark stack. – More efficient code, more efficient memory usage/spilling, more efficient serialization & networking, etc.  There are hardware and software technologies we can bring to the party. – We can tune the stack from hardware to high level structures for running Spark.  Spark and Scala developers can help themselves by their style of coding.  All the changes are being made in the Java runtime or being pushed out to the Spark community.  There is lots more stuff I don't have time to talk about, like GC optimizations, object layout, monitoring VM/Spark events, hardware compression, security, etc. etc. – mailto:­kit