peter lawrey ceo of higher frequency trading jax london 2015 java lambda puzzlers

47
Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Upload: corey-charles

Post on 17-Jan-2016

216 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Peter LawreyCEO of Higher Frequency TradingJAX London 2015

Java Lambda Puzzlers

Page 2: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Peter Lawrey

Java Developer/Consultant for investment banks, hedge fund and trading firms for 7 years.Most answers for Java and JVM on stackoverflow.comFounder of the Performance Java User’s Group.Architect of Chronicle Software

Page 3: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Chronicle SoftwareHelp companies migrate to high performance Java code.First large Java 8 project for client in production Dec 2014.Sponsor open source projects https://github.com/OpenHFTLicensed solutions Chronicle-Enterprise and Chronicle-FixOffer one week proof of concept workshops, advanced Java training, consulting and bespoke development.

Page 4: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Chronicle Software

FIX – sub-micro second FIX Engine.

Enterprise – Monitoring, Traffic Shaping, Security.

Journal – Custom Data Store, Key-Queue

Engine – Customisable Data Fabric, Reactive Live Queries.Queue – Persist every event Map – Persisted Key-ValueNetwork – Remote access Wire – YAML, Binary YAML,

JSON, CSV, Raw data.Threads – Low latency Bytes – 64-bit off heap native

+ memory mapped filesCore – Low level access to OS and JVM

Page 5: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

AgendaReading and Writing lambdas.How many objects does this create?Capturing vs non-capturing lambdas.How do you transform imperative code into the Stream API?Mixing Imperative & FunctionalQ & A.

Page 6: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Reading and Writing lambdasLambdas use type inference. This means the context alters the type of the expression used. It even alters the implied type of a lambda. In Java we are used to types being very verbose, but with lambdas we can get more magic.

Page 7: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Puzzler: Summing a fieldImperative Style.

Functional Style

But what about summing BigDecimal?

double sum = getResults().stream() .mapToDouble(Trade::getRequirement) .sum();

double sum = 0.0;for (Trade t : getResults()) sum += t.getRequirement();

Page 8: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Summing a field with BigDecimalReduce has three parts.The starting value e.g. empty list, 0 for add or 1 for multiplying.How to accumulate each input with the previous value.How to accumulate those results.

But what about summing BigDecimal?

BigDecimal sum = getResults().parallelStream() .reduce(BigDecimal.ZERO, // starting point for each thread (bd, t) -> bd.add(t.getRequirement()), // in each thread. BigDecimal::add); // combine the results from each thread.

Page 9: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Summing a field with BigDecimal (simplified)Even though you can use parallel thread easily, you probably shouldn’t. Also try not to do too much in each step.

BigDecimal sum = getResults().stream() .map(Trade::getRequirement) .reduce(BigDecimal.ZERO,

BigDecimal::add, BigDecimal::add);

Page 10: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Puzzler: lambda factoriesLambdas make it easy to create factories. Even factories for constants.

This can be great way to inject code into your library. But can you work out a type for this lambda?

IntSupplier num = () -> 1;DoubleSupplier rnd = Math::random;LongSupplier now = System::currentTimeMillis;Supplier<String> status = () -> "Users: " + getUserCount();Supplier<Map<String,String>> mapSupplier = TreeMap::new;

greater = a -> b -> -a >- b

Page 11: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Puzzler: lambda factoriesIn this example, a lambda returns a lambda.

While you can curry functions this way in Java, it’s more likely to be confusing than useful. Where ever I have used this I have ended up refactoring this out.

IntFunction<IntPredicate> lessThan = a -> b -> -a >- b;System.out.println(lessThan.apply(1).test(2));

Page 12: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Puzzler: Type magic

Why does adding return null; suppress an error about a checked exception?

ExecutorService es = Executors.newCachedThreadPool();es.submit(() -> { // No error Files.lines(Paths.get("data.text")).forEach(System.out::println); return null;});

ExecutorService es = Executors.newCachedThreadPool();es.submit(() -> { // Unhandled Exception: java.lang.IOException Files.lines(Paths.get("data.text")).forEach(System.out::println);});}

Page 13: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Puzzler: Type magic

Why does adding return null; suppress an error about a checked exception?

ExecutorService es = Executors.newCachedThreadPool();es.submit(() -> { // No error Files.lines(Paths.get("data.text")).forEach(System.out::println); return null;});

ExecutorService es = Executors.newCachedThreadPool();es.submit(() -> { // Unhandled Exception: java.lang.IOException Files.lines(Paths.get("data.text")).forEach(System.out::println);});}

Page 14: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Anti Puzzler: How many object does this create?

A common question is; How many Objects/Strings does the following program create? This sort of question has limited value asYour JVM is likely to create far more object than you imagine possible the first time.You JVM can optimise your code and create far less objects than you might imagine once it is optimised.

Page 15: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Anti Puzzler: How many object does this create?

Let us consider this simple program and see how many objects it creates.

a) 20 objectsb) 200 objectsc) 2,000 objectsd) 20,000 objects

public class HowManyObjects { public static void main(String... args) throws IOException { IntStream.of(1, 2, 3, 4) .forEach(System.out::println); // so we can get a heap dump. System.in.read(); }}

Page 16: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

jmap -histo 4600 | head -20

num #instances #bytes class name---------------------------------------------- 1: 1328 1623296 [I 2: 7016 578904 [C 3: 803 279424 [B 4: 2538 142128 jdk.internal.org.objectweb.asm.Item 5: 4684 112416 java.lang.String 6: 147 82800 [Ljdk.internal.org.objectweb.asm.Item; 7: 692 79024 java.lang.Class 8: 1249 58976 [Ljava.lang.Object; 9: 1290 39768 [Ljava.lang.Class; 10: 776 31040 java.lang.invoke.MethodType 11: 493 27608 java.lang.invoke.MemberName 12: 776 24832 java.lang.invoke.MethodType$ConcurrentWeakInternSet$WeakEntry 13: 106 23744 jdk.internal.org.objectweb.asm.MethodWriter 14: 676 16224 java.lang.StringBuilder 15: 167 14696 java.lang.reflect.Method 16: 209 13376 jdk.internal.org.objectweb.asm.Label 17: 405 12960 jdk.internal.org.objectweb.asm.Type 18: 74 12432 jdk.internal.org.objectweb.asm.ClassWriter 19: 211 11816 jdk.internal.org.objectweb.asm.AnnotationWriter 20: 465 11160 jdk.internal.org.objectweb.asm.ByteVector 21: 446 10704 java.lang.StringBuffer

Page 17: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

How do Lambdas help?

Lambdas are like anonymous inner classes, however they are assigned to static variables if they don’t capture anything.

public static Runnable helloWorld() { return () -> System.out.println("Hello World");}

public static Consumer<String> printMe() { // may create a new object each time = Garbage. return System.out::println; }

public static Consumer<String> printMe2() { return x -> System.out.println(x);}

Page 18: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

How does Java 8 help? Lambdas

When you call new on an anonymous inner classes, a new object is always created. Non capturing lambdas can be cached.

Runnable r1 = helloWorld();Runnable r2 = helloWorld();System.out.println(r1 == r2); // prints true

Consumer<String> c1 = printMe();Consumer<String> c2 = printMe();System.out.println(c1 == c2); // prints false

Consumer<String> c3 = printMe2();Consumer<String> c4 = printMe2();System.out.println(c3 == c4); // prints true

Page 19: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Serialization

Lambdas capture less scope. This means it doesn’t capture this unless it has to, but it can capture things you don’t expect.

Lambdas capture less scope. If you use this.a the value of a is copied.Note: if you use the :: notation, it will capture the left operand if it is a variable.

Page 20: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

interface SerializableConsumer<T> extends Consumer<T>, Serializable {}

// throws java.io.NotSerializableException: java.io.PrintStreampublic SerializableConsumer<String> printMe() { return System.out::println;}

public SerializableConsumer<String> printMe2() { return x -> System.out.println(x);}

public SerializableConsumer<String> printMe3() { // throws java.io.NotSerializableException: A return new SerializableConsumer<String>() { @Override public void accept(String s) { System.out.println(s); } };}

Page 21: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Why Serialize a Lambda?

Lambdas are designed to reduce boiler plate, and when you have a distributed system, they can be a powerful addition.

The two lambdas are serialized on the client to be executed on the server.

This example is from the RedisEmulator in Chronicle-Engine.

public static long incrby(MapView<String, Long> map, String key, long toAdd) { return map.syncUpdateKey(key, v -> v + toAdd, v -> v);}

Page 22: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Why Serialize a Lambda?

public static Set<String> keys(MapView<String, ?> map, String pattern) { return map.applyTo(m -> { Pattern compile = Pattern.compile(pattern); return m.keySet().stream() .filter(k -> compile.matcher(k).matches()) .collect(Collectors.toSet()); });}

The lambda m-> { is serialized and executed on the server.

Page 23: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

// print userId which have a usageCounter > 10 // each time it is incremented (asynchronously)

userMap.entrySet().query() .filter(e -> e.getValue().usageCounter > 10) .map(e -> e.getKey()) .subscribe(System.out::println);

Why Serialize a Lambda?

The filter/map lambdas are serialized.The subscribe lambda is executed asynchronously on the client.

Page 24: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Escape Analysis

Escape Analysis can-Determine an object doesn’t escape a method so it can be placed on the stack.-Determine an object doesn’t escape a method so it doesn’t need to be synchronized.

Page 25: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Escape Analysis

Escape Analysis works with inlining. After inlining, the JIT can see all the places an object is used. If it doesn’t escape the method it doesn’t need to be created and can be unpacked on the stack. This works for class objects, but not arrays currently.

After “unpacking” on to the stack the object might be optimised away. E.g. say all the fields are set using local variables anyway.

Page 26: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Escape Analysis

As of Java 8 update 60, the JITed code generated is still not as efficient as code written to no need these optimisation, however the JIT is getting closer to optimal.

Page 27: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

How does Java 8 help? Escape Analysis

To parameters which control in-lining and the maximum method size for performing escape analysis are

-XX:MaxBCEAEstimateSize=150 -XX:FreqInlineSize=325

For our software I favour

-XX:MaxBCEAEstimateSize=450 -XX:FreqInlineSize=425

Page 28: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Puzzler: What type is this?

message: Hello World number: 1234567890 code: SECONDS price: 10.5

greater = a -> b -> a > b

Page 29: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Puzzler: What type is this?

message: Hello World number: 1234567890 code: SECONDS price: 10.5

IntFunction<IntPredicate> greater = a -> b -> a > b;System.out.println(greater.apply(1).test(2)); // false

Page 30: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

A low latency API which uses Lambdas

Chronicle Wire is a single API which supports multiple formats. You decide what data you want to read/write and independently you can chose the format. E.g. YAML, JSON, Binary YAML, XML.

Using lambdas helped to simplify the API.

Page 31: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

A low latency API which uses Lambdas

Timings are in micro-seconds with JMH.

* Data was read/written to native memory.

Wire Format Bytes 99.9 %tile 99.99 %tile 99.999 %tile worst

JSONWire 100* 3.11 5.56 10.6 36.9

Jackson 100 4.95 8.3 1,400 1,500

Jackson + Chronicle-Bytes 100* 2.87 10.1 1,300 1,400

BSON 96 19.8 1,430 1,400 1,600

BSON + Chronicle-Bytes 96* 7.47 15.1 1,400 11,600

BOON Json 100 20.7 32.5 11,000 69,000

"price":1234,"longInt":1234567890,"smallInt":123,"flag":true,"text":"Hello World!","side":"Sell" 

Page 32: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

A resizable buffer and a Wire format

// Bytes which wraps a ByteBuffer which is resized as needed. Bytes<ByteBuffer> bytes = Bytes.elasticByteBuffer();// YAML based wire format Wire wire = new TextWire(bytes);

// or a binary YAML based wire formatBytes<ByteBuffer> bytes2 = Bytes.elasticByteBuffer(); Wire wire2 = new BinaryWire(bytes2);

// or just data, no meta data. Bytes<ByteBuffer> bytes3 = Bytes.elasticByteBuffer(); Wire wire3 = new RawWire(bytes3);

Page 33: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Low latency API using Lambdas (Wire)

message: Hello World number: 1234567890 code: SECONDS price: 10.5

wire.read(() -> "message").text(this, (o, s) -> o.message = s) .read(() -> "number").int64(this, (o, i) -> o.number = i) .read(() -> "timeUnit").asEnum(TimeUnit.class, this, (o, e) -> o.timeUnit = e) .read(() -> "price").float64(this, (o, d) -> o.price = d);

wire.write(() -> "message").text(message) .write(() -> "number").int64(number) .write(() -> "timeUnit").asEnum(timeUnit) .write(() -> "price").float64(price);

To write a message

To read a message

Page 34: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

A resizable buffer and a Wire format

message: Hello World number: 1234567890 code: SECONDS price: 10.5

In the YAML based TextWire

Binary YAML Wire

message: Hello World number: 1234567890 code: SECONDS price: 10.5

00000000 C7 6D 65 73 73 61 67 65 EB 48 65 6C 6C 6F 20 57 ·message ·Hello W 00000010 6F 72 6C 64 C6 6E 75 6D 62 65 72 A3 D2 02 96 49 orld·num ber····I 00000020 C4 63 6F 64 65 E7 53 45 43 4F 4E 44 53 C5 70 72 ·code·SE CONDS·pr 00000030 69 63 65 90 00 00 28 41 ice···(A

Page 35: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Lambdas and Junit tests

message: Hello World number: 1234567890 code: SECONDS price: 10.5

To read the data

To check the data without a data structure

wire.read(() -> "message").text(this, (o, s) -> o.message = s) .read(() -> "number").int64(this, (o, i) -> o.number = i) .read(() -> "timeUnit").asEnum(TimeUnit.class, this, (o, e) -> o.timeUnit = e) .read(() -> "price").float64(this, (o, d) -> o.price = d);

wire.read(() -> "message").text("Hello World", Assert::assertEquals) .read(() -> "number").int64(1234567890L, Assert::assertEquals) .read(() -> "timeUnit").asEnum(TimeUnit.class, TimeUnit.SECONDS,Assert::assertEquals) .read(() -> "price").float64(10.5, (o, d) -> assertEquals(o, d, 0));

Page 36: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Interchanging Enums and Lambdas

message: Hello World number: 1234567890 code: SECONDS price: 10.5

Enums and lambdas can both implement an interface.Wherever you have used a non capturing lambda you can also use an enum.

enum Field implements WireKey { message, number, timeUnit, price;}

@Overridepublic void writeMarshallable(WireOut wire) { wire.write(Field.message).text(message) .write(Field.number).int64(number) .write(Field.timeUnit).asEnum(timeUnit) .write(Field.price).float64(price);}

Page 37: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

When to use Enums

message: Hello World number: 1234567890 code: SECONDS price: 10.5

Enums have a number of benefits.They are easier to debug.The serialize much more efficiently.Its easier to manage a class of pre-defined enums to implement your code, than lambdas which could be any where

Under https://github.com/OpenHFT/Chronicle-Engine search for MapFunction and MapUpdater

Page 38: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

When to use Lambdas

message: Hello World number: 1234567890 code: SECONDS price: 10.5

Lambdas have a number of benefits.They are simpler to writeThey support generics betterThey can capture values.

Page 39: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Anonymous inner classes to lambdas

message: Hello World number: 1234567890 code: SECONDS price: 10.5

Use your IDE. I can transform from

to

IntFunction<IntPredicate> greater = new IntFunction<IntPredicate>() { @Override public IntPredicate apply(int a) { return new IntPredicate() { @Override public boolean test(int b) { return a > b; } }; }};

IntFunction<IntPredicate> greater = a -> b -> a > b;

Page 40: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Where can I try this out?

message: Hello World number: 1234567890 code: SECONDS price: 10.5

The source for these micro-benchmarks are test are availablehttps://github.com/OpenHFT/Chronicle-Wire

Chronicle Engine with live subscriptionshttps://github.com/OpenHFT/Chronicle-Engine

Page 41: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Mixing Imperative and Functional codeImperative Style.

Functional Style

double sum = getResults().stream() .mapToDouble(Trade::getRequirement) .sum();

double sum = 0.0;for (Trade t : getResults()) sum += t.getRequirement();

Page 42: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Mixing Imperative and Functional code

message: Hello World number: 1234567890 code: SECONDS price: 10.5

Reasons to not mix imperative and functional code.-Oracle says you should do it.-Most developers of functional languages will tell you not to do it.-When we reviewed cases where code was confusion or possibly in error, most of the time this was due to mixing imperative and functional coding.

list.stream() .filter(x -> x.isBad()) .forEach(list.remove());

Page 43: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Mixing Imperative and Functional code

message: Hello World number: 1234567890 code: SECONDS price: 10.5

Reasons you will end up doing this anyway.-Java has no language support for pure function, nor are they planned.-There is no code inspection tool I know of to help you inforce this.-There are times when using pure functional code is just far more complicated, possibly less efficient.

In short, avoid mixing styles if you can, and expecially avoid mixing styles by accident.

Page 44: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Mixing Imperative and Functional code

message: Hello World number: 1234567890 code: SECONDS price: 10.5

Reasons you will end up doing this anyway.-Java has no language support for identifying pure function or enforcing pure functional coding, nor is any planned.-There is no code inspection tool I know of to help you inforce this, though there probably should be and will be.-There are times when using pure functional code is just far more complicated, possibly less efficient.

In short, avoid mixing styles if you can, and expecially avoid mixing styles by accident.

Page 45: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Puzzler: compute two

message: Hello World number: 1234567890 code: SECONDS price: 10.5

map.computeIfAbsent(Key.Hello, s -> { map.computeIfAbsent(Key.Hello, t -> 1); return 2; });

enum Key {Hello}

Page 46: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Puzzler: compute two

message: Hello World number: 1234567890 code: SECONDS price: 10.5

HashMap: {Hello=2}WeakHashMap: {Hello=2}TreeMap: {Hello=2}IdentityHashMap: {Hello=2}EnumMap: {Hello=2}Hashtable: {Hello=2, Hello=1}LinkedHashMap: {Hello=1, Hello=2}ConcurrentSkipListMap: {Hello=1}ConcurrentHashMap: (Never returns)

Page 47: Peter Lawrey CEO of Higher Frequency Trading JAX London 2015 Java Lambda Puzzlers

Q & A

Peter Lawrey

@ChronicleUG

http://chronicle.software

@PeterLawrey

http://vanillajava.blogspot.com

IntStream.range(0, 128).parallel().forEach(System::exit);