+ All Categories
Transcript
Page 1: Linked to ArrayList: the full story

#Devoxx

LinkedList to ArrayListThe Full Story!

José Paumard

Page 2: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

It all begin…

• November 22nd 2015 by a tweet

Page 3: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

It all begin…

• Then the discussion started

Page 4: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

It all begin…

• And about 30 tweets later:

Page 5: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

And did not ended there!

• And around midnight, 7th nov. 2016:

• Since this is the subject of this Uni, we will take more time…

Page 6: Linked to ArrayList: the full story
Page 7: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Questions ?#ListJ9

Questions?#ListJ9

Page 8: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

• This discussion ArrayList vs LinkedList is no troll…

Even if it looks like one!

• The question is :

Is one of these implementations better than the other?

• And there is an answer to this question!

And the conclusion is…

Page 9: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

And the conclusion is…

• To fully understand this answer, we need to understand many other things

Page 10: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

And the conclusion is…

• To fully understand this answer, we need to understand many other things

• And once we have answered it (almost) fully , manyother questions will arise!

Page 11: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

1st part

• Algorithms

• Complexity

• Implementation

• CPU architecture

• « cache friendly »

• Slides, bullet points…

Page 12: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

2nd part

• Implementation of Set, List et Map (with a surprise!)

• Live coding!

• Some more slides but not that many

But don’t worry, still bullet points

Page 13: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Where to begin?

• In fact there are several ways to begin…

Page 14: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Where to begin?

• In fact there are several ways to begin…

• Algorithms!

Page 15: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Where to begin?

• In fact there are several ways to begin…

• Algorithms!

• Implementation vs CPU architecture

Page 16: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Where to begin?

• In fact there are several ways to begin…

• Algorithms!

• Implementation vs CPU architecture

• Use cases

Page 17: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

What do we know?

1) ArrayList is built on an array

2) LinkedList is built on a linked list

And we have results on this

Page 18: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Approach

• Take a close look at the basic operations on lists

• Create / modify:

Add an element at the end of at the beginning of a list

Removing an element by its index

Removing an element

The contains() method

The removeIf() and sort() methods

Page 19: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Approach

• Take a close look at the basic operations on lists

• Add:

List<String> list = ...

list.add("one"); // addlist.add(12, "one"); // insert

Page 20: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Approach

• Take a close look at the basic operations on lists

• Remove:

List<String> list = ...

list.remove("one"); // removelist.remove(12); // remove by index

Page 21: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Approach

• Take a close look at the basic operations on lists

• Sort / removeIf:

List<String> list = ...

list.sort(comparingBy(String::length)); // sortlist.removeIf(s -> s.length() > 10); // remove if

Page 22: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Approach

• Take a close look at the basic operations on lists

• Read operations:

Iterate

Read an element with its index

Building a Stream

Page 23: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Approach

• Take a close look at the basis operations on lists

• Read operations:

List<String> list = ...

list.forEach(System.out::println); // list traversallist.get(12); // access by indexlist.stream(); // stream creation

Page 24: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

ArrayList

• Built on an array

• Access to the nth element: almost free

• Adding an element: almost free, but…

• Insertion: shift the elements to the right

• Deletion: shift the elements to the left

Page 25: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

ArrayList

• Built on an array

• Access to the nth element: almost free

n

Page 26: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

ArrayList

• Built on an array

• Adding an element: almost free, but…

private void grow(int minCapacity) {int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);elementData = Arrays.copyOf(elementData, newCapacity);

}

Page 27: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

ArrayList

• Built on an array

• Adding an element: almost free, but…

• Sometimes the array has to be enlarge

• But we can fix the size of the array when we build it

Page 28: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

ArrayList

• Built on an array

• Adding an element: almost free, but…

• Sometimes the array has to be enlarge

• Bytheway growing a HashSet is much more expensive…

Page 29: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

ArrayList

• Built on an array

• Insertion

n, one

Page 30: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

ArrayList

• Built on an array

• Insertion

public void add(int index, E element) {System.arraycopy(elementData, index, elementData, index + 1,

size - index);elementData[index] = element;size++;

}

Page 31: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

ArrayList

• Built on an array

• Insertion

one

Page 32: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

ArrayList

• Built on an array

• Suppression

n

X

Page 33: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

ArrayList

• Built on an array

• Suppression

public E remove(int index) {E oldValue = elementData(index);System.arraycopy(elementData, index+1, elementData, index,

numMoved);return oldValue;

}

Page 34: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

ArrayList

• (Very) Early conclusion

• Accessing an element is fast!

• Adding is fast, if there is no overflow

• Random insert / delete: overhead to handle the « holes »

Page 35: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

LinkedList

• Built on a double linked list

• Access to the nth element

• Adding an element at the end of the list

• Insertion

• Deletion

Page 36: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

LinkedList

• Base structure

next prev

item

Node

next prev

item

Node

next prev

item

Node

first last

Page 37: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

LinkedList

• Built on a double linked list

• Access to the nth element: run through all the elements

• Adding: free, just a modification of pointers

• Insertion: free, just a modification of pointers

• Suppression: free, just a modification of pointers

Page 38: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

LinkedList

• Access to the nth element

Node<E> node(int index) {if (index < (size >> 1)) {

Node<E> x = first;for (int i = 0; i < index; i++)

x = x.next;return x;

}// same for last

}

Page 39: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

LinkedList

• Access to the nth element: one has to visit all the elements and count them, until the nth is found

• We say that the complexity of this operation is « N »

• Or is O(n)

Page 40: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Notation O(N)

• This notation means that:

if we multiply the amount of elements to be processed by 2

then the number of operations will also be multiplied by 2

Page 41: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Notation O(N)

• The complexity of an algorithm is given by O(f(n))

• Example: f(n) = n2

Page 42: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Notation O(N)

• The complexity of an algorithm is given by O(f(n))

• Example: f(n) = n2

• In fact, it means that the exact number of operations is

f(n) = an2 + bn + g

• And when n reaches a given value

f(n) ~ n2

Page 43: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Notation O(N)

• All right, but processing data is not anly about maths

• In our applications, n has a value, the same for a, band g…

• And the theory might not apply very well to our use case

Page 44: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Complexity comparison

log2(n)

1 0

10 3

100 7

1 000 10

1 000 000 20

Page 45: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Complexity comparison

log2(n) n

1 0 1

10 3 10

100 7 100

1 000 10 1 000

1 000 000 20 1 000 000

Page 46: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Complexity comparison

log2(n) n n x log2(n)

1 0 1 0

10 3 10 30

100 7 100 700

1 000 10 1 000 10 000

1 000 000 20 1 000 000 20 000 000

Page 47: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Complexity comparison

log2(n) n n x log2(n) n2

1 0 1 0 1

10 3 10 30 100

100 7 100 700 10 000

1 000 10 1 000 10 000 1 000 000

1 000 000 20 1 000 000 20 000 000 too much!

Page 48: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Complexity comparison

« too much » means that on any CPU, running a thousand billion of operations will take at least 20mn

• So we need to parallelize this operations of a large number of CPU (at least a few thousands)

We need to change our algorithm

We are not running this on a single JVM

Page 49: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Lists complexity

• On the basic operations

ArrayList LinkedList

add(e) 1

add(index, e) 1

set(index, e) 1

remove(index) 1

iterator() 1

Page 50: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Lists complexity

• On the basic operations

ArrayList LinkedList

add(e) 1 1

add(index, e) 1 N

set(index, e) 1 N

remove(index) 1 N

iterator() 1 1

Page 51: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Lists complexity

• We still must be cautious…

• There are System.arrayCopy() in ArrayList that are not here in LinkedList

• Is it a g? Or something that grows with n? And how?

Page 52: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Lists complexity

• We still must be cautious…

• Example: if we consider add(index, e), isSystem.arrayCopy() more costly than going throughthe pointers of the LinkedList?

• Are there any hidden costs that we did not see?

Page 53: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• There is a standard tool (Java 9) to bench Java code : JMH

• A benchmark is an annotated class

• Maven can generate a « transformed » JAR

• Then we run this JAR to get the result of the bench

Page 54: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Here is a « bench » class

@Warmup(iterations=5, time=10, timeUnit=TimeUnit.MILLISECONDS)@Measurement(iterations=10, time=10, timeUnit=TimeUnit.MILLISECONDS)@Fork(value=1,

jvmArgsAppend = {"-XX:+UseParallelGC", "-Xms3g", "-Xmx3g"})@BenchmarkMode(Mode.AverageTime)@OutputTimeUnit(TimeUnit.NANOSECONDS)@State(Scope.Benchmark)public class Bench {

// bench}

Page 55: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• POM dependencies, version is 1.15

<dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-core</artifactId><version>${jmh.version}</version> <!-- 1.15 -->

</dependency><dependency>

<groupId>org.openjdk.jmh</groupId><artifactId>jmh-generator-annprocess</artifactId><version>${jmh.version}</version><scope>provided</scope>

</dependency>

Page 56: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• In the class

@Param({"10", "100", "1000"})private int size;

@Param({LINKED_LIST, ARRAY_LIST})private String type;

Page 57: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• In the class

@Setuppublic void setup() {

switch(type) {case ARRAY_LIST :

list = IntStream.range(0, size).mapToObj(Integer::toString).collect(Collectors.toCollection(

() -> new ArrayList<String>(size))); break;// other cases

}

Page 58: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Simple add operation:

Can trigger a System.arrayCopy() on ArrayList

Fast on LinkedList since there is a pointer to the end of the list

@Benchmarkpublic boolean simpleAdd() {

return list.add("one more");}

Page 59: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Simple add with index:

Triggers at least one System.arrayCopy() one ArrayList

Slower on LinkedList since half of the list must be visited

@Benchmarkpublic boolean simpleAdd() {

list.add(size / 2, "one more");return true;

}

Page 60: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Results for Simple Add

LinkedList ArrayList Big ArrayList

10 5,3 ns 3,6 ns 3,6 ns

100 5,3 ns 3,6 ns 3,7 ns

1 000 5,3 ns 3,7 ns 3,7 ns

1 000 000 5,5 ns 3,7 ns 3,7 ns

Page 61: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Index Read :

Read operation in the middle of the list

We expect the worst performance for LinkedList

@Benchmarkpublic boolean indexRead() {

return list.get(size / 2);}

Page 62: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Index Set:

Write operation in the middle of the list

We also expect the worst performance for LinkedList

@Benchmarkpublic boolean indexSet() {

return list.set(size / 2, "one more");}

Page 63: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Results for Index Read

LinkedList ArrayList

10 5,5 ns 2,8 ns

100 37,9 ns 2,8 ns

1 000 596 ns 2,8 ns

1 000 000 3,4 ms 2,8 ns

Page 64: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Results for Index Set

LinkedList ArrayList

10 5,5 ns 3,4 ns

100 36,0 ns 3,5 ns

1 000 602 ns 3,5 ns

1 000 000 3,4 ms 3,5 ns

Page 65: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Iteration :

Let us iterate over the whole list

1st pattern: iterator

@Benchmarkpublic long iterate() {

long sum = 0;for (String s : list) {

sum += s.length();}return sum;

}

Page 66: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Iteration :

Let us iterate over the whole list

2nd pattern: stream

@Benchmarkpublic long streamSum() {

return list.stream().mapToLong(String::length).sum();

}

Page 67: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Results for Iterate

LinkedList ArrayList

10 14,7 ns 11,6 ns

100 127 ns 84,3 ns

1 000 2,07 ms 895 ns

1 000 000 6,2 ms 3,3 ms

Page 68: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Benchmarks!

• Results for Stream

LinkedList ArrayList

10 33 ns 64 ns

100 174 ns 440 ns

1 000 1,57 ms 3,6 ms

1 000 000 6,1 ms 4,8 ms

Page 69: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

removeIf() and sort()

• Let us check the code

Page 70: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

First conclusion

• The overhead of the System.arrayCopy() is not thatbad

It can be avoided in the case of growing lists

• That been said, there is still an unexpectedperformance difference, that we need to explain

Page 71: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

CPU architecture

• The multicores CPU work in a special way…

• Between the main memory and the ALU (arithmetic & logic computation) there are at least 3 levels of cache, and registers

Page 72: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

CPU architecture

Core 1Access to the registers< 1ns

Page 73: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

CPU architecture

Core 1

L1

Access to the registers< 1ns

Access time ~1ns32kB data / code

Page 74: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

CPU architecture

Core 1

L1

Access to the registers< 1ns

Access time ~1ns32kB data / code

L2Access time ~3ns256 kB

Page 75: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

CPU architecture

Core 1

L1

L2

Core N

L1

L2

Page 76: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

CPU architecture

Core 1

L1

L2

L3 (~12ns)

Core N

L1

L2

Page 77: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

CPU architecture

C 1

L1

L2

L3

C N

L1

L2

C 1

L1

L2

L3

C N

L1

L2

Page 78: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

CPU architecture

C 1

L1

L2

L3

QPI

C N

L1

L2

C 1

L1

L2

L3

QPI

C N

L1

L2

~40 ns

Page 79: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

CPU architecture

C 1

L1

L2

L3

QPI

DRAM (~65 ns)

C N

L1

L2

C 1

L1

L2

L3

QPI

C N

L1

L2

~40 ns

Page 80: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

So…

• Writing good algorithms is not enough…

• They also need to be adapted to this structure!

Page 81: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

So…

• Writing good algorithms is not enough…

• They also need to be adapted to this structure!

• What makes a processing fast is its capacity to bringthe processed data in the L1 cache as fast as possible

Page 82: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

The ennemy is the cache miss!

• A cache miss occurs when the CPU needs a data that isnot in the L1 cache

• This data has to be fetched somewhere else

• In the worst case: in main memory

• A cache miss is a loss of about 500 instructions

Page 83: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Structure of the L1 cache

• The L1 cache is organized in lines

• Each line is 8 long = 64 bytes

• The data is transfered line by line between the caches and the main memory

• And here lies a big difference between ArrayList and LinkedList…

Page 84: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Transferring a list in L1

• The elements of an ArrayList are stored in contiguous zones of the memory, since it is an array

• Where the nodes of a LinkedList are randomlyspread…

• So transferring an ArrayList in the L1 cache can be 8 times faster than a LinkedList (at least)

Page 85: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Pointer chasing

• This phenomenon is called « pointer chasing », and itcan kill performances!

Page 86: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Pointer chasing

• Example

int[] tab = {1, 2, 3, 4, 5};Integer[] tab = {1, 2, 3, 4, 5};

Page 87: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Pointer chasing

• Example

int[] tab = {1, 2, 3, 4, 5};Integer[] tab = {1, 2, 3, 4, 5};

1 2 3 4 5

Page 88: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Pointer chasing

• Example

int[] tab = {1, 2, 3, 4, 5};Integer[] tab = {1, 2, 3, 4, 5};

1 2 3 4 5

12

4

5

3

Page 89: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Pointer chasing

• Example

public class Point {Integer x, y;

}

List<Point> points = new ArrayList<>();

Page 90: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Pointer chasing

• Example

public class Point {Integer x, y;

}

List<Point> points = new ArrayList<>();

x

y1

2

Page 91: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

• Example

• With such a structure, an algorithm will spend all itstime chasing pointers…

Pointer chasing

public class Point {Integer x, y;

}

List<Point> points = new ArrayList<>();

x

y1

2

Page 92: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Pointer chasing

• Clearly, the LinkedList structure is not adapted to the structure of CPU

• It is not a « cache friendly » structure

Page 93: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Pointer chasing

• An other example: HashMap

What is a HashMap? An array of Map.Entry objects

Page 94: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Pointer chasing

• An other example: HashMap

What is a HashMap? An array of Map.Entry objects

key

value

Page 95: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Pointer chasing

• An other example: HashMap

What is a HashMap? An array of Map.Entry objects

key

value

12

« twelve »

Page 96: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

• An other example: HashMap

What is a HashMap? An array of Map.Entry objects

Pointer chasing

key

value

12

« twelve »

Page 97: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

• An other example: HashMap

What is a HashMap? An array of Map.Entry objects

• The same goes for HashSet…

Pointer chasing

key

value

12

« twelve »

Page 98: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Some more benchmarks

• Results for Iterate

HashSet ArrayList

10 26,2 ns 11,6 ns

100 223 ns 84,3 ns

1 000 3,4 ms 895 ns

1 000 000 11 ms 3,3 ms

Page 99: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

• In our bench, the LinkedList is built like that:

• It means that out Node elements are still in contiguousplaces in memory…

A closer look at our bench

list = IntStream.range(0, size).mapToObj(Integer::toString).collect(toCollection(LinkedList::new));

Page 100: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

• What about building our LinkedList like that:

• No more contiguous nodes…

A closer look at our bench

list = IntStream.range(0, size).mapToObj(i -> IntStream.range(i, i + 100)

.mapToObj(Integer::toString)

.collect(toList()) ).map(l -> l.get(0)) .collect(toCollection(LinkedList::new));

Page 101: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A closer look at our bench

• Results for Indexed Iterate

LinkedList Sparse LinkedList

10 14,7 ns 16,7 ns

100 127 ns 393 ns

1 000 2,07 ms 11 ms

1 000 000 6,2 ms 18 ms

Page 102: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A closer look at our bench

• Results for Indexed Read

LinkedList Sparse LinkedList

10 5,5 ns 5,4 ns

100 37,9 ns 69 ns

1 000 596 ns 2,0 ms

1 000 000 3,4 ms 15,3 ms

Page 103: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A closer look at our bench

• Results for Indexed Set

LinkedList Sparse LinkedList

10 5,5 ns 6,2 ns

100 36,0 ns 83,7 ns

1 000 602 ns 2,0 ms

1 000 000 3,4 ms 14,0 ms

Page 104: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Any other way?

• In a not-so-distant future: List<int> !

Project Valhalla: http://mail.openjdk.java.net/pipermail/valhalla-spec-experts/

• Motto:

Codes like a class, works like an int!

Page 105: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Any other way?

• In the much closer present:

Eclipse Collections (prev. GS Collections)

HPPC

Koloboke

Trove

http://java-performance.info/hashmap-overview-jdk-fastutil-goldman-sachs-hppc-koloboke-trove-january-2015/

Page 106: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A list without object?

• Is it possible to create implementations of Set / List / Map without any pointer chasing?

Page 107: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A list without object?

• Construire des implémentations de Set / List / Mapsans pointer chasing ?

• Pointer chasing comes from objects… Can we get rid of objects in those implementations?

Page 108: Linked to ArrayList: the full story

#DevoxxJosé Paumard

Factory methods for Collections in Java 9Live coding: unexpected implementations of

lists, sets, maps

Page 109: Linked to ArrayList: the full story

#Devoxx

José Paumard

Page 110: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

So where do we want to go?

• Starting point: Java 9!

• Question: how to create a list with objects in it

• Answer: not easily done in Java 8…

Page 111: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A glimpse at Java 9

• Until Java 8: (mustache syntax)

• And if you want the immutable version, you need to create in two steps…

Map<Integer, String> map = new HashMap<Integer, String>() {{put(1, "one") ;put(2, "two") ;put(3, "three") ;

}} ;

Page 112: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A glimpse at Java 9

• New patterns in Java 9 for lists and sets:

The implementations are immutables

Null elements are not allowed (NPE)

The Set throws an IllegalArgumentException in case of a double

Serializable…

List<Integer> list = List.of(1, 2, 3) ;Set<Integer> set = Set.of(1, 2, 3) ;

Page 113: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A glimpse at Java 9

• New patterns in Java 9 for lists and sets:

• But they are also:

Randomly iterable

And with optimized implementations

List<Integer> list = List.of(1, 2, 3) ;Set<Integer> set = Set.of(1, 2, 3) ;

Page 114: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A glimpse at Java 9

• Optimized implementations:public static List<E> of(E e1);public static List<E> of(E e1, E e2);public static List<E> of(E e1, E e2, E e3);public static List<E> of(E e1, E e2, E e3, E e4);...public static List<E> of(E... e);

Page 115: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A glimpse at Java 9

• In the case of Mappublic static Map<K, V> of(K key1, V value1);public static Map<K, V> of(K key1, V value1, K key2, V value2);...public static Map<K, V> of(K... K, V... v);

Page 116: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A glimpse at Java 9

• In the case of Map

ofEntries() throws an exception in the case of a double key

public static Map<K, V> of(K key1, V value1);public static Map<K, V> of(K key1, V value1, K key2, V value2);...public static Map<K, V> of(K... K, V... v);

public static Entry<K, V> of(K key, V value);

public static Map<K, V> ofEntries(Entry... e);

Page 117: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A glimpse at Java 9

Map<String, TokenType> tokens =Map.ofEntries(

entry("for", KEYWORD),entry("while", KEYWORD),entry("do", KEYWORD),entry("break", KEYWORD),entry(":", COLON),entry("+", PLUS),entry("--‐", MINUS),entry(">", GREATER),entry("<", LESS),entry(":", PAAMAYIM_NEKUDOTAYIM),entry("(", LPAREN),entry(")", RPAREN)

); © Stuart Mark

Page 118: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

A glimpse at Java 9

Map<String, TokenType> tokens =Map.ofEntries(

entry("for", KEYWORD),entry("while", KEYWORD),entry("do", KEYWORD),entry("break", KEYWORD),entry(":", COLON),entry("+", PLUS),entry("--‐", MINUS),entry(">", GREATER),entry("<", LESS),entry(":", PAAMAYIM_NEKUDOTAYIM),entry("(", LPAREN),entry(")", RPAREN)

); © Stuart Mark

Page 119: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

What do we want to do?

• Question: how can we build an optimizedimplementation of a list with 1 or 2 elements?

• Set, List and Map (and Map.Entry)

• Of course we want to minimize pointer chasing

Page 120: Linked to ArrayList: the full story

#Devoxx

Don’t worry, they’ll be back

And bullet points too

Page 121: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Some more bench

• Results for indexed read

ArrayList SingletonList TwoElementList

1 3,5 ns 2,7 ns

2 2,9 ns 2,6 ns

Page 122: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Some more bench

• Results for Iterate

ArrayList SingletonList TwoElementList

1 4,7 ns 2,3 ns

2 6,5 ns 3,4 ns

Page 123: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Some more bench

• Results for Iterate

HashSet SingletonSet TwoElementSet

1 7,3 ns 2,3 ns

2 9,5 ns 3,5 ns

Page 124: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Conclusion

• By Stuart Marks:

Page 125: Linked to ArrayList: the full story

#Devoxx #ListJ9 @JosePaumard

Conclusion

• Many patterns from the GoF can be implemented withlambdas

• We saw it for the template method

• And used it for lists, sets and maps…

But should be avoided!

• Avoiding pointer chasing = best performances!

Page 126: Linked to ArrayList: the full story

#Devoxx

José Paumard

Page 127: Linked to ArrayList: the full story

#Devoxx

Thank you!

José Paumard


Top Related