From 3e635ff64d1255119fc58758c3f1a234dc1dc1e9 Mon Sep 17 00:00:00 2001 From: Rob Austin Date: Mon, 10 Nov 2014 14:46:03 +0000 Subject: [PATCH 1/3] updated readme --- README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 63d93f9dd..8b19350b2 100644 --- a/README.md +++ b/README.md @@ -424,7 +424,10 @@ BondVOInterface bond = map.get(key); bond.getCoupon() ``` -lets say that it is only the `coupon` field that we are interested in, then its better not to have to deserialize the whole object that implements the `BondVOInterface`. It would be more efficient to just deserialize the field(s) that we require, in this example just the coupon. This is why we created the`OffHeapUpdatableChronicleMapBuilder`, when you use this builder chronicle will not create on heap copies of your whole DataStructure. You can instead use proxy objects that can read and write into the off heap data structures, this reduced serialisation can give you a big performance boost. Below we show you how you can work directly with the off heap entries. +lets say that it is only the `coupon` field that we are interested in, then its better not to have to +deserialize the whole object that implements the `BondVOInterface`. It would be more efficient to just +deserialize the field(s) that we require, in this example just the coupon. This is why we created +the`OffHeapUpdatableChronicleMapBuilder`, when you use this builder chronicle will not create on heap copies of your whole data structure. You can instead use proxy objects that can read and write into the off heap data structures, this reduced serialisation can give you a big performance boost. Below we show you how you can work directly with the off heap entries. ``` java ChronicleMap chm = OffHeapUpdatableChronicleMapBuilder @@ -443,13 +446,14 @@ value class, in our case `BondVOInterface.class` is an `interface` rather than a class that class has to extend `Byteable`), now like before, we can use the `getUsing(key,using)` method, but this time we have to create the ‘using’ instance slightly -differently +differently, we have to call the `newDirectReference(..)` method. ``` java BondVOInterface using = DataValueClasses.newDirectReference(BondVOInterface.class); ``` -now we can call +the call to get using is the same + ``` java BondVOInterface bond = map.getUsing(key,value); @@ -457,8 +461,8 @@ BondVOInterface bond = map.getUsing(key,value); this won’t create any on heap objects, and it won’t deserialize the ‘bond’ entry from off heap to on heap, all it does is sets the bond as a proxy to the off heap memory, this proxy object was created by -`DataValueClasses.newDirectReference(BondVOInterface.class)` it allows us access the fileds for our entry -directly from the off heap storage, +`DataValueClasses.newDirectReference(BondVOInterface.class)`, it allows us access to the fields of our +entry, directly into the off heap storage. so when you call : @@ -470,7 +474,7 @@ its only the coupon that gets deserialized. Just like any other concurrent map, chronicle map uses segment locking, if you wish to obtain a read lock -when calling when calling getUsing(key,using) you can do this : +when calling getUsing(key,using) you can do this : ``` java try (ReadContext context = map.getUsingLocked(key,using)) { @@ -497,7 +501,7 @@ If you wish not to use a try block you must manually release the segment lock by context.close() // the lock will get released when this is called ``` -#### what acquireUsingLocked() +#### acquireUsingLocked() just like getUsing(), acquireUsing() will also recycle the value you pass it, the following code is a pattern that you will often come across, acquireUsing(key,value) offers this @@ -542,7 +546,7 @@ try (WriteContext context = map.acquireUsingLocked("one", bo } ``` -if after you have read the 'issueDate' and 'symbol' you wish to remove the entry based on some +if after you have read the 'issueDate' and 'symbol' and you wish to remove the entry based on some business logic, it more efficient to use the 'context' to remove the entry, as the contents is already aware when the entry is in memory. From b47454a02a1b0d1d482128250b3c2617e897e7d8 Mon Sep 17 00:00:00 2001 From: Rob Austin Date: Mon, 10 Nov 2014 14:58:36 +0000 Subject: [PATCH 2/3] updated readme --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8b19350b2..c238b2483 100644 --- a/README.md +++ b/README.md @@ -340,7 +340,7 @@ a key, but unlike `map.getUsing()` if there is not an entry in the map for this will be added and the value return will we the same value which you provided. ( The section below covers both `map.getUsing()` and `map.acquireUsing()` in more detail ) -#### Off Heap Storage and how using a Proxy Object can Improve Performance +#### Off heap storage and how using a Proxy Object can improve performance Chronicle Map stores its data off heap. There are some distinct advantages in using off heap data storage @@ -350,7 +350,7 @@ Chronicle Map stores its data off heap. There are some distinct advantages in us heap memory. When this memory is shared between processes on the same server, the chronicle map is able to distribute entries between processes with extremely low overhead, as both processes are sharing the same off heap memory space. Since your objects are already stored off -heap ( as a series of bytes ), replicating entries over the network add relatively low over head. +heap ( as a series of bytes ), replicating entries over the network adds relatively low over head. One of the downsides of an Off Heap Map is that whenever you wish to get a value ( on heap ) from an entry which is off heap, for example calling : @@ -364,6 +364,7 @@ that entry has to be deserialised onto the java heap so that you can use its val ``` java for(int i=1;i<=10;i++) { Value v = get(key) + } ``` this would create 10 separate instances of the value. As each time get() is called the map has to @@ -380,7 +381,8 @@ and existing object ( in this case the ‘using’ value ), you can instead call Value using = new Value(); for(int i=1;i<=10;i++) { - Value v = map.getUsing(key,using); // this won’t create a new value each time. + Value bond = map.getUsing(key,using); // this won’t create a new value each time. + assert using == bond; // this will always be the same instance and the Vaule type can not be immutable } ``` We can get a further performance improvement if we don't deserialize the whole object, @@ -452,11 +454,12 @@ differently, we have to call the `newDirectReference(..)` method. BondVOInterface using = DataValueClasses.newDirectReference(BondVOInterface.class); ``` -the call to get using is the same - +the call to `getUsing(key,value)` is the same as we had in the earlier example ``` java -BondVOInterface bond = map.getUsing(key,value); +BondVOInterface using = DataValueClasses.newDirectReference(BondVOInterface.class); +BondVOInterface bond = map.getUsing(key,using); +assert using == bond; // this will always be the same instance ``` this won’t create any on heap objects, and it won’t deserialize the ‘bond’ entry from off heap to on @@ -472,13 +475,12 @@ bond.getCoupon() its only the coupon that gets deserialized. - Just like any other concurrent map, chronicle map uses segment locking, if you wish to obtain a read lock when calling getUsing(key,using) you can do this : ``` java -try (ReadContext context = map.getUsingLocked(key,using)) { - BondVOInterface bond = context.value().getValue(); +BondVOInterface bond = map.getUsing(key,value); +try (ReadContext context = map.getUsingLocked(key,bond)) { long issueDate = bond.getIssueDate(); String symbol = bond.getSymbol(); From 1774a4d3311085f93cae44122368d0b2c4808f50 Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Mon, 10 Nov 2014 15:07:08 +0000 Subject: [PATCH 3/3] Tweek segment sizes again. --- .../map/AbstractChronicleMapBuilder.java | 36 +++++++++---------- .../chronicle/map/ChronicleMapTest.java | 3 +- .../chronicle/map/EntryCountMapTest.java | 36 +++++++++---------- 3 files changed, 36 insertions(+), 39 deletions(-) diff --git a/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java index a89942f33..e0a689148 100644 --- a/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java @@ -372,10 +372,8 @@ public abstract class AbstractChronicleMapBuilder 100 ? procs / 2 : procs; + int threads = procs * 3; ExecutorService es = Executors.newFixedThreadPool(procs); for (int runs : new int[]{10, 50, 250, 500, 1000, 2500}) { // JAVA 8 produces more garbage than previous versions for internal work. @@ -585,6 +585,7 @@ public class ChronicleMapTest { OffHeapUpdatableChronicleMapBuilder builder = OffHeapUpdatableChronicleMapBuilder .of(CharSequence.class, LongValue.class) .entries(entries) + .actualSegments(8 * 1024) .entryAndValueAlignment(OF_8_BYTES) .entrySize(24); diff --git a/src/test/java/net/openhft/chronicle/map/EntryCountMapTest.java b/src/test/java/net/openhft/chronicle/map/EntryCountMapTest.java index d3cad018a..742366961 100644 --- a/src/test/java/net/openhft/chronicle/map/EntryCountMapTest.java +++ b/src/test/java/net/openhft/chronicle/map/EntryCountMapTest.java @@ -53,7 +53,7 @@ public class EntryCountMapTest { throws IOException { OffHeapUpdatableChronicleMapBuilder mapBuilder = OffHeapUpdatableChronicleMapBuilder.of(CharSequence.class, LongValue.class) .entries(entries) - .minSegments(segments) + .actualSegments(segments) .entrySize(entrySize) .entryAndValueAlignment(alignment); return mapBuilder.createPersistedTo(getPersistenceFile()); @@ -103,33 +103,33 @@ public class EntryCountMapTest { testEntriesMaxSize(s, 2000, 3100, i); testEntriesMaxSize(s, 4000, 5100, i); testEntriesMaxSize(s, 5000, 7100, i); - testEntriesMaxSize(s, 8000, 10200, i); - testEntriesMaxSize(s, 12000, 16000, i); - testEntriesMaxSize(s, 16000, 21300, i); + testEntriesMaxSize(s, 8000, 11000, i); + testEntriesMaxSize(s, 12000, 17000, i); + testEntriesMaxSize(s, 16000, 22000, i); s = 32; testEntriesMaxSize(s, 2000, 3900, i); testEntriesMaxSize(s, 4000, 5900, i); testEntriesMaxSize(s, 5000, 7900, i); testEntriesMaxSize(s, 8000, 12000, i); - testEntriesMaxSize(s, 12000, 15900, i); - testEntriesMaxSize(s, 16000, 21300, i); - testEntriesMaxSize(s, 32000, 42200, i); + testEntriesMaxSize(s, 12000, 17000, i); + testEntriesMaxSize(s, 16000, 23000, i); + testEntriesMaxSize(s, 32000, 46000, i); s = 64; testEntriesMaxSize(s, 5000, 7550, i); testEntriesMaxSize(s, 8000, 11400, i); - testEntriesMaxSize(s, 12000, 16000, i); - testEntriesMaxSize(s, 16000, 22000, i); - testEntriesMaxSize(s, 32000, 43000, i); - testEntriesMaxSize(s, 40000, 54000, i); + testEntriesMaxSize(s, 12000, 18000, i); + testEntriesMaxSize(s, 16000, 24000, i); + testEntriesMaxSize(s, 32000, 47000, i); + testEntriesMaxSize(s, 40000, 60000, i); s = 128; - testEntriesMaxSize(s, 16000, 23000, i); - testEntriesMaxSize(s, 32000, 44200, i); - testEntriesMaxSize(s, 40000, 56000, i); - testEntriesMaxSize(s, 64000, 92000, i); - testEntriesMaxSize(s, 129000, 190000, i); + testEntriesMaxSize(s, 16000, 24000, i); + testEntriesMaxSize(s, 32000, 50000, i); + testEntriesMaxSize(s, 40000, 62000, i); + testEntriesMaxSize(s, 64000, 98000, i); + testEntriesMaxSize(s, 129000, 204000, i); s = 256; - testEntriesMaxSize(s, 64000, 92000, i); - testEntriesMaxSize(s, 129000, 192000, i); + testEntriesMaxSize(s, 64000, 100000, i); + testEntriesMaxSize(s, 129000, 213000, i); } // hyperbolic average gives more weight to small numbers. System.out.printf("Score: %.2f%n", scoreCount / score);