Slide 1

Slide 1 text

Wir unternehmen IT. Efficient use of multi-core processors with lambdas and streams Dr. Fabian Stäber

Slide 2

Slide 2 text

Slide 09.09.2014 www.consol.de 2 λ λ λ λ λ

Slide 3

Slide 3 text

Slide Example 26 March 2014 www.consol.de 3 Business Data Aggregation

Slide 4

Slide 4 text

Slide Example • Boilerplate – pom.xml – RestConfig (modern web.xml) • Model – Contract – State enum – TestDataGenerator • View – JavaScript, HTML • Controller – Dashboard. 26 March 2014 www.consol.de 4

Slide 5

Slide 5 text

Slide • Boilerplate – pom.xml – RestConfig (modern web.xml) • Model – Contract – State enum – TestDataGenerator • View – JavaScript, HTML • Controller – Dashboard. 4.0.0 de.consol.research lambda 1.0-SNAPSHOT war 1.8 1.8 false javax javaee-api 7.0 lambda Example 26 March 2014 www.consol.de 5 javax javaee-web-api 7.0

Slide 6

Slide 6 text

Slide package de.consol.research; import ... @ApplicationPath("api") public class RestConfig extends Application {} Example • Boilerplate – pom.xml – RestConfig (modern web.xml) • Model – Contract – State enum – TestDataGenerator • View – JavaScript, HTML • Controller – Dashboard. 26 March 2014 www.consol.de 6 @ApplicationPath("api") public class RestConfig extends Application {}

Slide 7

Slide 7 text

Slide Example • Boilerplate – pom.xml – RestConfig (modern web.xml) • Model – Contract – State enum – TestDataGenerator • View – JavaScript, HTML • Controller – Dashboard. 26 March 2014 www.consol.de 7 package de.consol.research; import java.time.LocalDate; public class Contract { private final State state; private final int priceInCent; private final LocalDate date; public Contract(State state, int priceInCent, LocalDate date) { this.state = state; this.priceInCent = priceInCent; this.date = date; } public State getState() { return state; } public int getPriceInCent() { return priceInCent; } public LocalDate getDate() { return date; } } public class Contract { private final State state; private final int priceInCent; private final LocalDate date;

Slide 8

Slide 8 text

Slide Example • Boilerplate – pom.xml – RestConfig (modern web.xml) • Model – Contract – State enum – TestDataGenerator • View – JavaScript, HTML • Controller – Dashboard. 26 March 2014 www.consol.de 8 package de.consol.research; public enum State { BW, // Baden-Württemberg BY, // Bayern BE, // Berlin BB, // Brandenburg HB, // Bremen HH, // Hamburg HE, // Hessen MV, // Mecklenburg-Vorpommern NI, // Niedersachsen NW, // Nordrhein-Westfalen RP, // Rheinland-Pfalz SL, // Saarland SN, // Sachsen ST, // Sachsen-Anhalt SH, // Schleswig-Holstein TH // Thüringen } public enum State { BW, // Baden-Württemberg BY, // Bayern BE, // Berlin BB, // Brandenburg

Slide 9

Slide 9 text

Slide Example • Boilerplate – pom.xml – RestConfig (modern web.xml) • Model – Contract – State enum – TestDataGenerator • View – JavaScript, HTML • Controller – Dashboard. 26 March 2014 www.consol.de 9 package de.consol.research; import ... public class TestDataGenerator { public static List makeTestData() { return Arrays.asList( new Contract(BY, 2000, now()), new Contract(BY, 3500, now().minusDays(1)), new Contract(NW, 7000, now().minusMonths(1)), new Contract(HE, 400, now().minusMonths(1) .minusDays(2)) ); } } public class TestDataGenerator { public static List makeTestData() { return Arrays.asList( new Contract(BY, 2000, now()), new Contract(BY, 3500, now().minusDays(1)), new Contract(NW, 7000, now().minusMonths(1)),

Slide 10

Slide 10 text

Slide Example • Boilerplate – pom.xml – RestConfig (modern web.xml) • Model – Contract – State enum – TestDataGenerator • View – JavaScript, HTML • Controller – Dashboard. 26 March 2014 www.consol.de 10

Slide 11

Slide 11 text

Slide Example • Boilerplate – pom.xml – RestConfig (modern web.xml) • Model – Contract – State enum – TestDataGenerator • View – JavaScript, HTML • Controller – Dashboard. 26 March 2014 www.consol.de 11 package de.consol.research; import ... @Path("dashboard") public class Dashboard { private List contracts = TestDataGenerator.makeTestData(); } @Path("dashboard") public class Dashboard { private List contracts = TestDataGenerator.makeTestData(); }

Slide 12

Slide 12 text

Slide Stream API 26 March 2014 www.consol.de 12 Business Data Aggregation with Stream API

Slide 13

Slide 13 text

Slide GET /lambda/api/dashboard/total-sales Stream API 26 March 2014 www.consol.de 13 Task 1: Total Sales 12900

Slide 14

Slide 14 text

Slide Stream API 26 March 2014 www.consol.de 14 @GET @Path("total-sales") @Produces(APPLICATION_JSON) public int totalSales() { return contracts .stream() .filter(contract -> contract.getDate().isAfter(now().minusYears(1))) .mapToInt(Contract::getPriceInCent) .sum(); }

Slide 15

Slide 15 text

Slide Stream API 26 March 2014 www.consol.de 15 Task 2: Sales by State GET /lambda/api/dashboard/sales-by-state { "BY": 5500, "NW": 7000, "HE": 400 }

Slide 16

Slide 16 text

Slide Stream API 26 March 2014 www.consol.de 16 @GET @Path("sales-by-state") @Produces(APPLICATION_JSON) public Map salesByState() { return contracts .stream() .filter(contract -> contract.getDate().isAfter(now().minusYears(1))) .collect(Collectors.groupingBy( Contract::getState, Collectors.summingInt(Contract::getPriceInCent))); }

Slide 17

Slide 17 text

Slide Stream API 26 March 2014 www.consol.de 17 @GET @Path("sales-by-state") @Produces(APPLICATION_JSON) public Map salesByState() { return contracts .stream() .filter(contract -> contract.getDate().isAfter(now().minusYears(1))) .collect(Collectors.groupingBy( Contract::getState, Collectors.summingInt(Contract::getPriceInCent))); } Can we re-use Lambda expressions?

Slide 18

Slide 18 text

Slide Stream API 26 March 2014 www.consol.de 18 private Predicate last12months = contract -> contract.getDate().isAfter(now().minusYears(1)); @GET @Path("sales-by-state") @Produces(APPLICATION_JSON) public Map salesByState() { return contracts .stream() .filter(last12months) .collect(Collectors.groupingBy( Contract::getState, Collectors.summingInt(Contract::getPriceInCent))); }

Slide 19

Slide 19 text

Slide GET /lambda/api/dashboard/sales-per-month Stream API 26 March 2014 www.consol.de 19 Task 3: Sales per Month { “2014-03": 5500, “2014-02": 7400 }

Slide 20

Slide 20 text

Slide Stream API 26 March 2014 www.consol.de 20 @GET @Path("sales-per-month") @Produces(APPLICATION_JSON) public Map salesPerMonth() { return contracts .stream() .filter(last12months) .collect(Collectors.groupingBy( c -> c.getDate().getYear() + "-" + c.getDate().getMonthValue(), Collectors.summingInt(Contract::getPriceInCent))); }

Slide 21

Slide 21 text

Slide Stream API Stream API 26 March 2014 www.consol.de 21 Get things done in few lines of code… …really  ?

Slide 22

Slide 22 text

Slide Stream API 26 March 2014 www.consol.de 22 public int totalSales() { return contracts .stream() .mapToInt(Contract::getPriceInCent) .sum(); } public int totalSales() { int resultInCent = 0; for (Contract contract : contracts) { resultInCent += contract.getPriceInCent(); } return resultInCent; } Java 8 Java 7

Slide 23

Slide 23 text

Slide Stream API 26 March 2014 www.consol.de 23 Why all that hype?

Slide 24

Slide 24 text

Slide Stream API 26 March 2014 www.consol.de 24 Faster !!!

Slide 25

Slide 25 text

Slide How To Make Things Faster • Keep Data in RAM • Avoid DB Roundtrips • Snappy Lightweight AJAX Front-End • Use Multiple Cores 26 March 2014 www.consol.de 25    ? ?

Slide 26

Slide 26 text

Slide 09.09.2014 www.consol.de 26 λ λ λ λ λ

Slide 27

Slide 27 text

Slide Use Multiple Cores parallelize 26 March 2014 www.consol.de 27

Slide 28

Slide 28 text

Slide Use Multiple Cores 26 March 2014 www.consol.de 28 public int totalSales() { return contracts .stream() .mapToInt(Contract::getPriceInCent) .sum(); } public int totalSales() { int resultInCent = 0; for (Contract contract : contracts) { resultInCent += contract.getPriceInCent(); } return resultInCent; } Java 8 Java 7

Slide 29

Slide 29 text

Slide Use Multiple Cores 26 March 2014 www.consol.de 29 public int totalSales() { return contracts .stream() .mapToInt(Contract::getPriceInCent) .sum(); } public int totalSales() { int resultInCent = 0; for (Contract contract : contracts) { resultInCent += contract.getPriceInCent(); } return resultInCent; } Do It Yourself synchronized, volatile, Lock, Executor, Callable, … Let the Library Do It For You

Slide 30

Slide 30 text

Slide Use Multiple Cores 26 March 2014 www.consol.de 30 public int totalSales() { return contracts .parallelStream() .mapToInt(Contract::getPriceInCent) .sum(); } public int totalSales() { int resultInCent = 0; for (Contract contract : contracts) { resultInCent += contract.getPriceInCent(); } return resultInCent; } Do It Yourself synchronized, volatile, Lock, Executor, Callable, … Let the Library Do It For You

Slide 31

Slide 31 text

Slide Use Multiple Cores 26 March 2014 www.consol.de 31 // Set of all states in contract data Set states = new HashSet(); contracts .parallelStream() .map(Contract::getState) .forEach(s -> states.add(s)); Thread Safe Operations Not Thread Safe

Slide 32

Slide 32 text

Slide Use Multiple Cores 26 March 2014 www.consol.de 32 // Set of all states in contract data Set states = contracts .parallelStream() .map(Contract::getState) .collect(Collectors.toSet()); Thread Safe Operations Thread Safe 

Slide 33

Slide 33 text

Slide Use Multiple Cores Under the Hood (internal stream API implementation) 26 March 2014 www.consol.de 33

Slide 34

Slide 34 text

Slide Under the Hood 26 March 2014 www.consol.de 34 (2 + 8 + 7 + 3) + (3 + 5 + 1 + 7) (2 + 8) + (7 + 3) 7 + 3 2 + 8 3 + 5 1 + 7 (3 + 5) + (1 + 7)

Slide 35

Slide 35 text

Slide Under the Hood 26 March 2014 www.consol.de 35 if (my portion of the work is small enough) do the work directly else split my work into two pieces invoke the two pieces and wait for the results Java 7: Fork/Join Framework

Slide 36

Slide 36 text

Slide Under the Hood 26 March 2014 www.consol.de 36 public class LegacyContractsData implements Iterable { private State[] states; private int[] pricesInCent; private LocalDate[] dates; // ... } Can I use this with my legacy data?

Slide 37

Slide 37 text

Slide Under the Hood Tell the stream API: • how to split your data into chunks • how to iterate over a chunk. 26 March 2014 www.consol.de 37 Implement a Spliterator. Can I use this with my legacy data?

Slide 38

Slide 38 text

Slide Under the Hood 26 March 2014 www.consol.de 38 public class MyOwnSpliterator implements Spliterator { @Override public Spliterator trySplit() { // split data into chunks } @Override public boolean tryAdvance(Consumer action) { // iterate over a chunk } @Override public long estimateSize() { // optional metadata } @Override public int characteristics() { // optional metadata } }

Slide 39

Slide 39 text

Slide Use Multiple Cores 26.03.2014 www.consol.de 39 Awesome! Amazing! But: How much faster is it really?

Slide 40

Slide 40 text

Slide Use Multiple Cores Benchmarking 26 March 2014 www.consol.de 40

Slide 41

Slide 41 text

Slide public static void main(String[] args) { long start = System.nanoTime(); for (int i=0; i<10; i++) { dashboard.totalSales(); // sequential version } long duration = System.nanoTime() - start; System.out.println("10 sequential calls took " + duration + "ns."); // ... same with parallel version here. } Use Multiple Cores 26.03.2014 www.consol.de 41 Naive and probably wrong. vm warmup dead code elimination constant folding

Slide 42

Slide 42 text

Slide Use Multiple Cores 26.03.2014 www.consol.de 42 JMH to the Rescue! @Benchmark @BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.MILLISECONDS) public int myBenchmark() { return dashboard.totalSales(); }

Slide 43

Slide 43 text

Slide Use Multiple Cores 26.03.2014 www.consol.de 43 10 Contracts: Sequential 8 times better 10,000 Contracts: Parallel 2 times better

Slide 44

Slide 44 text

Slide 09.09.2014 www.consol.de 44 λ λ λ λ λ λ

Slide 45

Slide 45 text

Slide Summary 26 March 2014 www.consol.de 45 Efficient use of multi-core processors with lambdas and streams: • Library does it for you • Use thread safe operations • Implement Spliterators • Evaluate your results

Slide 46

Slide 46 text

Slide Summary 26 March 2014 www.consol.de 46 Slides: https://speakerdeck.com/fstab/ Example code: https://github.com/consol/ Twitter: @fstabr