diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java index 13bb53994..64d52cfc0 100644 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java @@ -120,7 +120,7 @@ public class ChronicleMapBuilder implements Cloneable { return this; } - public int minSegments() { + int minSegments() { return minSegments < 1 ? tryMinSegments(4, 65536) : minSegments; } @@ -139,7 +139,8 @@ public class ChronicleMapBuilder implements Cloneable { * *

If key size varies moderately, specify the size higher than average, but lower than * the maximum possible, to minimize average memory overuse. If key size varies in a wide range, - * it's better to use {@link #entrySize() entry size} in "chunk" mode and configure it directly. + * it's better to use {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it + * directly. * *

Example: if keys in your map(s) are English words in {@link String} form, keys size 10 * (a bit more than average English word length) would be a good choice:

{@code
@@ -157,7 +158,7 @@ public class ChronicleMapBuilder implements Cloneable {
      * @return this {@code ChronicleMapBuilder} back
      * @see #constantKeySizeBySample(Object)
      * @see #valueSize(int)
-     * @see #entrySize()
+     * @see #entrySize(int)
      */
     public ChronicleMapBuilder keySize(int keySize) {
         if (keySize <= 0)
@@ -208,13 +209,13 @@ public class ChronicleMapBuilder implements Cloneable {
      *
      * 

If value size varies moderately, specify the size higher than average, but lower than * the maximum possible, to minimize average memory overuse. If value size varies in a wide - * range, it's better to use {@link #entrySize() entry size} in "chunk" mode and configure - * it directly. + * range, it's better to use {@linkplain #entrySize(int) entry size} in "chunk" mode and + * configure it directly. * * @param valueSize number of bytes, taken by serialized form of values * @return this {@code ChronicleMapBuilder} back * @see #keySize(int) - * @see #entrySize() + * @see #entrySize(int) */ public ChronicleMapBuilder valueSize(int valueSize) { if (valueSize <= 0) @@ -258,28 +259,7 @@ public class ChronicleMapBuilder implements Cloneable { } /** - * Configures the {@linkplain #entrySize() entry size}. - * - *

Note that the actual entrySize will be aligned to 4 (default {@linkplain - * #entryAndValueAlignment() entry alignment}). I. e. if you set entry size to 30, the actual - * entry size will be 32 (30 aligned to 4 bytes). If you don't want entry size to be aligned, - * set {@code entryAndValueAlignment(Alignment.NO_ALIGNMENT)}. - * - * @param entrySize the size in bytes - * @return this {@code ChronicleMapBuilder} back - * @see #entrySize() - * @see #entryAndValueAlignment(Alignment) - * @see #entryAndValueAlignment() - */ - public ChronicleMapBuilder entrySize(int entrySize) { - if (entrySize <= 0) - throw new IllegalArgumentException("Entry Size must be positive"); - this.entrySize = entrySize; - return this; - } - - /** - * Returns the size in bytes of allocation unit of {@code ChronicleMap} instances, created by + * Configures the size in bytes of allocation unit of {@code ChronicleMap} instances, created by * this builder. * *

{@code ChronicleMap} stores it's data off-heap, so it is required to serialize @@ -298,7 +278,7 @@ public class ChronicleMapBuilder implements Cloneable { *

If entry size is not {@linkplain #entrySize(int) configured} explicitly, it is computed * based on {@linkplain #metaDataBytes() meta data bytes}, plus {@linkplain #keySize(int) key * size}, plus {@linkplain #valueSize(int) value size}, plus a few bytes required by - * implementations, with respect to {@linkplain #entryAndValueAlignment() alignment}. + * implementations, with respect to {@linkplain #entryAndValueAlignment(Alignment) alignment}. * *

There are three major patterns of this configuration usage: *

    @@ -324,7 +304,7 @@ public class ChronicleMapBuilder implements Cloneable { * {@link IllegalArgumentException} is thrown on attempt to insert too large entry, * compared to the configured or computed entry size. *
  1. The number of "entries" {@linkplain #entries(long) in the whole map} and - * {@linkplain #actualEntriesPerSegment() per segment} is actually the number + * {@linkplain #actualEntriesPerSegment(int) per segment} is actually the number * "chunks".
  2. * * @@ -346,10 +326,24 @@ public class ChronicleMapBuilder implements Cloneable { * *
* - * @return size of memory allocation unit (in bytes) + *

Note that the actual entrySize will be aligned to 4 (default {@linkplain + * #entryAndValueAlignment(Alignment) entry alignment}). I. e. if you set entry size to 30, + * the actual entry size will be 32 (30 aligned to 4 bytes). If you don't want entry size + * to be aligned, set {@code entryAndValueAlignment(Alignment.NO_ALIGNMENT)}. + * + * @param entrySize the size in bytes + * @return this {@code ChronicleMapBuilder} back + * @see #entryAndValueAlignment(Alignment) * @see #entries(long) */ - public int entrySize() { + public ChronicleMapBuilder entrySize(int entrySize) { + if (entrySize <= 0) + throw new IllegalArgumentException("Entry Size must be positive"); + this.entrySize = entrySize; + return this; + } + + int entrySize() { if (entrySize > 0) return entryAndValueAlignment().alignSize(entrySize); int size = metaDataBytes; @@ -385,38 +379,60 @@ public class ChronicleMapBuilder implements Cloneable { * volatile access, because it doesn't work well if the value crosses cache lines. Also, on some * (nowadays rare) architectures any misaligned memory access is more expensive than aligned. * - *

Note that {@link #entrySize()} will be aligned according to this alignment. - * I. e. if you set {@code entrySize(20)} and {@link Alignment#OF_8_BYTES}, actual entry size - * will be 24 (20 aligned to 8 bytes). + *

Note that {@linkplain #entrySize(int) entry size} will be aligned according to this + * alignment. I. e. if you set {@code entrySize(20)} and {@link Alignment#OF_8_BYTES}, actual + * entry size will be 24 (20 aligned to 8 bytes). + * + *

Default is {@link Alignment#OF_4_BYTES}. * * @param alignment the new alignment of the maps constructed by this builder * @return this {@code ChronicleMapBuilder} back - * @see #entryAndValueAlignment() */ public ChronicleMapBuilder entryAndValueAlignment(Alignment alignment) { this.alignment = alignment; return this; } - /** - * Returns alignment strategy of addresses in memory of entries and independently of values - * within entries in ChronicleMaps, created by this builder. - * - *

Default is {@link Alignment#OF_4_BYTES}. - * - * @return entry/value alignment strategy - * @see #entryAndValueAlignment(Alignment) - */ - public Alignment entryAndValueAlignment() { + Alignment entryAndValueAlignment() { return alignment; } + /** + * Configures the maximum number of {@linkplain #entrySize(int) "entry size chunks"}, which + * could be taken by the maximum number of entries, inserted into the maps, created by this + * builder. If you try to insert more data, {@link IllegalStateException} might be thrown, + * because currently {@code ChronicleMap} doesn't support resizing. + * + *

    + *
  1. If key and value sizes are constant, this number is equal to the maximum number + * of entries (because each entry takes exactly one "entry size" memory unit).
  2. + *
  3. If key and/or value size varies moderately, you should pass to this method + * the maximum number of entries + 5-25%, depending on your data properties and configured + * {@linkplain #keySize(int) key}/{@linkplain #valueSize(int) value}/{@linkplain + * #entrySize(int) entry} sizes.
  4. + *
  5. If your data size varies in a wide range, pass the maximum number of entries + * multiplied by average data size and divided by the configured "entry size" (i. e. chunk + * size). See an example in the documentation to {@link #entrySize(int)} method.
  6. + *
+ * + *

You shouldn't put additional margin over the number, computed according the rules above. + * This bad practice was popularized by {@link HashMap#HashMap(int)} constructor, which accept + * "capacity", that should be multiplied by "load factor" to obtain actual maximum expected + * number of entries. {@code ChronicleMap} doesn't have a notion of load factor. + * + *

Default value is 2^20 (~ 1 million). + * + * @param entries maximum size of the created maps, in memory allocation units, + * so-called "entry size" + * @return this builder back + * @see #entrySize(int) + */ public ChronicleMapBuilder entries(long entries) { this.entries = entries; return this; } - public long entries() { + long entries() { return entries; } @@ -425,7 +441,7 @@ public class ChronicleMapBuilder implements Cloneable { return this; } - public int actualEntriesPerSegment() { + int actualEntriesPerSegment() { if (actualEntriesPerSegment > 0) return actualEntriesPerSegment; int as = actualSegments(); @@ -438,7 +454,7 @@ public class ChronicleMapBuilder implements Cloneable { return this; } - public int actualSegments() { + int actualSegments() { if (actualSegments > 0) return actualSegments; if (!largeSegments && entries > (long) minSegments() << 15) { @@ -456,7 +472,7 @@ public class ChronicleMapBuilder implements Cloneable { return this; } - public long lockTimeOut(TimeUnit unit) { + long lockTimeOut(TimeUnit unit) { return unit.convert(lockTimeOut, lockTimeOutUnit); } @@ -465,63 +481,62 @@ public class ChronicleMapBuilder implements Cloneable { return this; } - public MapErrorListener errorListener() { + MapErrorListener errorListener() { return errorListener; } /** - * Map.put() returns the previous value, functionality which is rarely used but fairly cheap for HashMap. - * In the case, for an off heap collection, it has to create a new object (or return a recycled one) - * Either way it's expensive for something you probably don't use. + * Configures if the maps created by this builder should return {@code null} instead of previous + * mapped values on {@link ChronicleMap#put(Object, Object) ChornicleMap.put(key, value)} calls. * - * @param putReturnsNull false if you want ChronicleMap.put() to not return the object that was replaced - * but instead return null - * @return an instance of the map builder + *

{@link Map#put(Object, Object) Map.put()} returns the previous value, functionality which + * is rarely used but fairly cheap for {@link HashMap}. In the case, for an off heap collection, + * it has to create a new object and deserialize the data from off-heap memory. It's expensive + * for something you probably don't use. + * + *

By default, of cause, {@code ChronicleMap} conforms the general {@code Map} contract + * and returns the previous mapped value on {@code put()} calls. + * + * @param putReturnsNull {@code true} if you want {@link ChronicleMap#put(Object, Object) + * ChronicleMap.put()} to not return the value that was replaced + * but instead return {@code null} + * @return an instance of the map builder back + * @see #removeReturnsNull(boolean) */ public ChronicleMapBuilder putReturnsNull(boolean putReturnsNull) { this.putReturnsNull = putReturnsNull; return this; } - /** - * Map.put() returns the previous value, functionality which is rarely used but fairly cheap for HashMap. - * In the case, for an off heap collection, it has to create a new object (or return a recycled one) - * Either way it's expensive for something you probably don't use. - * - * @return true if ChronicleMap.put() is not going to return the object that was replaced but instead - * return null - */ - public boolean putReturnsNull() { + boolean putReturnsNull() { return putReturnsNull; } /** - * Map.remove() returns the previous value, functionality which is rarely used but fairly cheap for - * HashMap. In the case, for an off heap collection, it has to create a new object (or return a recycled - * one) Either way it's expensive for something you probably don't use. + * Configures if the maps created by this builder should return {@code null} instead of + * the last mapped value on {@link ChronicleMap#remove(Object) ChronicleMap.remove(key)} calls. * - * @param removeReturnsNull false if you want ChronicleMap.remove() to not return the object that was - * removed but instead return null - * @return an instance of the map builder + *

{@link Map#remove(Object) Map.remove()} returns the previous value, functionality which + * is rarely used but fairly cheap for {@link HashMap}. In the case, for an off heap collection, + * it has to create a new object and deserialize the data from off-heap memory. It's expensive + * for something you probably don't use. + * + * @param removeReturnsNull {@code true} if you want {@link ChronicleMap#remove(Object) + * ChronicleMap.remove()} to not return the value of the removed entry + * but instead return {@code null} + * @return an instance of the map builder back + * @see #putReturnsNull(boolean) */ public ChronicleMapBuilder removeReturnsNull(boolean removeReturnsNull) { this.removeReturnsNull = removeReturnsNull; return this; } - /** - * Map.remove() returns the previous value, functionality which is rarely used but fairly cheap for - * HashMap. In the case, for an off heap collection, it has to create a new object (or return a recycled - * one) Either way it's expensive for something you probably don't use. - * - * @return true if ChronicleMap.remove() is not going to return the object that was removed but instead - * return null - */ - public boolean removeReturnsNull() { + boolean removeReturnsNull() { return removeReturnsNull; } - public boolean largeSegments() { + boolean largeSegments() { return entries > 1L << (20 + 15) || largeSegments; } @@ -537,19 +552,21 @@ public class ChronicleMapBuilder implements Cloneable { return this; } - public int metaDataBytes() { + int metaDataBytes() { return metaDataBytes; } @Override public String toString() { return "ChronicleMapBuilder{" + - "actualSegments=" + actualSegments() + - ", minSegments=" + minSegments() + - ", actualEntriesPerSegment=" + actualEntriesPerSegment() + - ", keySize=" + keySize() + - ", valueSize=" + valueSize() + - ", entrySize=" + entrySize() + + "actualSegments=" + pretty(actualSegments) + + ", minSegments=" + pretty(minSegments) + + ", actualEntriesPerSegment=" + pretty(actualEntriesPerSegment) + + ", keySize=" + pretty(keySize) + + ", sampleKeyForConstantSizeComputation=" + pretty(sampleKey) + + ", valueSize=" + pretty(valueSize) + + ", sampleValueForConstantSizeComputation=" + pretty(sampleValue) + + ", entrySize=" + pretty(entrySize) + ", entryAndValueAlignment=" + entryAndValueAlignment() + ", entries=" + entries() + ", lockTimeOut=" + lockTimeOut + " " + lockTimeOutUnit + @@ -557,17 +574,26 @@ public class ChronicleMapBuilder implements Cloneable { ", errorListener=" + errorListener() + ", putReturnsNull=" + putReturnsNull() + ", removeReturnsNull=" + removeReturnsNull() + - ", largeSegments=" + largeSegments() + + ", largeSegments=" + (largeSegments ? "true" : "not configured") + ", timeProvider=" + timeProvider() + - ", bytesMarshallerfactory=" + bytesMarshallerFactory() + + ", bytesMarshallerFactory=" + pretty(bytesMarshallerFactory) + + ", objectSerializer=" + pretty(objectSerializer) + ", keyBuilder=" + keyBuilder + ", valueBuilder=" + valueBuilder + ", eventListener=" + eventListener + ", defaultValue=" + defaultValue + - ", defaultValueProvider=" + defaultValueProvider + + ", defaultValueProvider=" + pretty(defaultValueProvider) + '}'; } + private static String pretty(int value) { + return value > 0 ? value + "" : "not configured"; + } + + private static String pretty(Object obj) { + return obj != null ? obj + "" : "not configured"; + } + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @Override public boolean equals(Object o) { @@ -611,11 +637,11 @@ public class ChronicleMapBuilder implements Cloneable { return this; } - public TimeProvider timeProvider() { + TimeProvider timeProvider() { return timeProvider; } - public BytesMarshallerFactory bytesMarshallerFactory() { + BytesMarshallerFactory bytesMarshallerFactory() { return bytesMarshallerFactory == null ? bytesMarshallerFactory = new VanillaBytesMarshallerFactory() : bytesMarshallerFactory; @@ -626,7 +652,7 @@ public class ChronicleMapBuilder implements Cloneable { return this; } - public ObjectSerializer objectSerializer() { + ObjectSerializer objectSerializer() { return objectSerializer == null ? objectSerializer = BytesMarshallableSerializer.create( bytesMarshallerFactory(), JDKObjectSerializer.INSTANCE) : @@ -684,7 +710,7 @@ public class ChronicleMapBuilder implements Cloneable { return this; } - public MapEventListener> eventListener() { + MapEventListener> eventListener() { return eventListener; } diff --git a/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java b/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java index 15212dac7..c71134b82 100644 --- a/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java +++ b/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java @@ -61,10 +61,6 @@ public class ChronicleSetBuilder implements Cloneable { return this; } - public int actualSegments() { - return chronicleMapBuilder.actualSegments(); - } - /** * Set minimum number of segments. See concurrencyLevel in {@link java.util.concurrent.ConcurrentHashMap}. * @@ -76,20 +72,11 @@ public class ChronicleSetBuilder implements Cloneable { return this; } - public int minSegments() { - return chronicleMapBuilder.minSegments(); - } - public ChronicleSetBuilder actualElementsPerSegment(int actualEntriesPerSegment) { chronicleMapBuilder.actualEntriesPerSegment(actualEntriesPerSegment); return this; } - public int actualElementsPerSegment() { - return chronicleMapBuilder.actualEntriesPerSegment(); - } - - /** * Configures the optimal number of bytes for an element. If elementSize is always the same, call {@link * #constantElementsSizeBySample(Object)} method instead of this one. @@ -156,51 +143,22 @@ public class ChronicleSetBuilder implements Cloneable { return this; } - /** - * * Returns the alignment memory strategy used for addresses of elements - * - *

Default is {@link net.openhft.chronicle.map.Alignment#OF_4_BYTES}. - * - * @return alignment memory strategy - * @see #alignment(net.openhft.chronicle.map.Alignment) - */ - public Alignment alignment() { - return chronicleMapBuilder.entryAndValueAlignment(); - } - public ChronicleSetBuilder elements(long entries) { chronicleMapBuilder.entries(entries); return this; } - public long elements() { - return chronicleMapBuilder.entries(); - } - public ChronicleSetBuilder lockTimeOut(long lockTimeOut, TimeUnit unit) { chronicleMapBuilder.lockTimeOut(lockTimeOut, unit); return this; } - public long lockTimeOut(TimeUnit unit) { - return chronicleMapBuilder.lockTimeOut(unit); - } - public ChronicleSetBuilder errorListener(MapErrorListener errorListener) { chronicleMapBuilder.errorListener(errorListener); return this; } - public MapErrorListener errorListener() { - return chronicleMapBuilder.errorListener(); - } - - - public boolean largeSegments() { - return chronicleMapBuilder.largeSegments(); - } - public ChronicleSetBuilder largeSegments(boolean largeSegments) { chronicleMapBuilder.largeSegments(largeSegments); return this; @@ -211,23 +169,10 @@ public class ChronicleSetBuilder implements Cloneable { return this; } - public int metaDataBytes() { - return chronicleMapBuilder.metaDataBytes(); - } - @Override public String toString() { - return " ChronicleMapBuilder{" + - "actualSegments=" + actualSegments() + - ", minSegments=" + minSegments() + - ", actualEntriesPerSegment=" + actualElementsPerSegment() + - ", alignment=" + alignment() + - ", entries=" + elements() + - ", metaDataBytes=" + metaDataBytes() + - ", errorListener=" + errorListener() + - ", largeSegments=" + largeSegments() + - ", timeProvider=" + timeProvider() + - ", bytesMarshallerfactory=" + bytesMarshallerFactory() + + return " ChronicleSetBuilder{" + + "chronicleMapBuilder=" + chronicleMapBuilder + '}'; } @@ -257,23 +202,11 @@ public class ChronicleSetBuilder implements Cloneable { return this; } - public TimeProvider timeProvider() { - return chronicleMapBuilder.timeProvider(); - } - - public BytesMarshallerFactory bytesMarshallerFactory() { - return chronicleMapBuilder.bytesMarshallerFactory(); - } - public ChronicleSetBuilder bytesMarshallerFactory(BytesMarshallerFactory bytesMarshallerFactory) { chronicleMapBuilder.bytesMarshallerFactory(bytesMarshallerFactory); return this; } - public ObjectSerializer objectSerializer() { - return chronicleMapBuilder.objectSerializer(); - } - public ChronicleSetBuilder objectSerializer(ObjectSerializer objectSerializer) { chronicleMapBuilder.objectSerializer(objectSerializer); return this; diff --git a/src/test/java/eg/BigData.java b/src/test/java/eg/BigData.java index fd2cc894f..a40dde663 100644 --- a/src/test/java/eg/BigData.java +++ b/src/test/java/eg/BigData.java @@ -50,7 +50,6 @@ public class BigData { if (!new File("/ocz/tmp").exists()) dir = "."; String chmPath = dir + "/testmap-" + Long.toString(System.nanoTime(), 36); new File(chmPath).deleteOnExit(); - System.out.println("ChronicleMap entries() = " + builder.entries()); try { theMap = builder.create(new File(chmPath)); } catch (IOException e) { diff --git a/src/test/java/net/openhft/chronicle/map/blackboard/CHMTest5.java b/src/test/java/net/openhft/chronicle/map/CHMTest5.java similarity index 98% rename from src/test/java/net/openhft/chronicle/map/blackboard/CHMTest5.java rename to src/test/java/net/openhft/chronicle/map/CHMTest5.java index 1a322f450..b7c9543cc 100644 --- a/src/test/java/net/openhft/chronicle/map/blackboard/CHMTest5.java +++ b/src/test/java/net/openhft/chronicle/map/CHMTest5.java @@ -14,10 +14,8 @@ * limitations under the License. */ -package net.openhft.chronicle.map.blackboard; +package net.openhft.chronicle.map; -import net.openhft.chronicle.map.ChronicleMap; -import net.openhft.chronicle.map.ChronicleMapBuilder; import net.openhft.lang.model.DataValueClasses; import net.openhft.lang.model.constraints.MaxSize; diff --git a/src/test/java/net/openhft/chronicle/map/blackboard/CHMTestIterator1.java b/src/test/java/net/openhft/chronicle/map/CHMTestIterator1.java similarity index 93% rename from src/test/java/net/openhft/chronicle/map/blackboard/CHMTestIterator1.java rename to src/test/java/net/openhft/chronicle/map/CHMTestIterator1.java index 6d7d4e96a..9de26c9a7 100644 --- a/src/test/java/net/openhft/chronicle/map/blackboard/CHMTestIterator1.java +++ b/src/test/java/net/openhft/chronicle/map/CHMTestIterator1.java @@ -14,10 +14,7 @@ * limitations under the License. */ -package net.openhft.chronicle.map.blackboard; - -import net.openhft.chronicle.map.ChronicleMap; -import net.openhft.chronicle.map.ChronicleMapBuilder; +package net.openhft.chronicle.map; import java.io.File; import java.util.Map;