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

Post on 17-Jan-2016

216 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Peter LawreyCEO of Higher Frequency TradingJAX 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

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.

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

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.

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.

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();

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.

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);

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

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));

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);});}

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);});}

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.

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(); }}

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

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);}

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

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.

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); } };}

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);}

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.

// 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.

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.

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.

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.

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

Puzzler: What type is this?

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

greater = a -> b -> a > b

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

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.

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" 

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);

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

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

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));

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);}

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

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.

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;

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

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();

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());

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.

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.

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}

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)

Q & A

Peter Lawrey

@ChronicleUG

http://chronicle.software

@PeterLawrey

http://vanillajava.blogspot.com

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

top related