diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java index 46a780a7e..518f3a5aa 100644 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java @@ -57,25 +57,25 @@ import static net.openhft.chronicle.map.Objects.builderEquals; * ChronicleMapBuilder builder = ChronicleMapBuilder * .of(Key.class, Value.class) * .entries(100500); - * + *

* ChronicleMap map1 = builder.create(); * ChronicleMap map2 = builder.create();} * i. e. created {@code ChronicleMap} instances don't depend on the builder. - * + *

*

Use static {@link #of(Class, Class)} method to obtain a {@code ChronicleMapBuilder} instance. - * + *

*

{@code ChronicleMapBuilder} is mutable, see a note in {@link net.openhft.chronicle.common.ChronicleHashBuilder} * interface documentation. - * + *

*

Later in this documentation, "ChronicleMap" means "ChronicleMaps, created by {@code * ChronicleMapBuilder}", unless specified different, because theoretically someone might provide * {@code ChronicleMap} implementations with completely different properties. - * + *

*

{@code ChronicleMap} ("ChronicleMaps, created by {@code ChronicleMapBuilder}") currently * doesn't support resizing. That is why you should always configure {@linkplain * #entries(long) number of entries} you are going to insert into the created map at most. * See {@link #entries(long)} method documentation for more information on this. - * + *

*

{@code ChronicleMap} allocates memory by equally sized chunks. This size is called {@linkplain * #entrySize(int) entry size}, you are strongly recommended to configure it to achieve least memory * consumption and best speed. See {@link #entrySize(int)} method documentation for more information @@ -174,7 +174,7 @@ public class ChronicleMapBuilder implements Cloneable, private int tryMinSegments(int min, int max) { for (int i = min; i < max; i <<= 1) { - if (i * i * i >= entrySize() * 2) + if (i * i * i >= entries * 5 / 4) return i; } return max; @@ -182,7 +182,7 @@ public class ChronicleMapBuilder implements Cloneable, /** * {@inheritDoc} - * + *

*

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
      * ChronicleMap wordFrequencies = ChronicleMapBuilder
@@ -209,7 +209,7 @@ public class ChronicleMapBuilder implements Cloneable,
 
     /**
      * {@inheritDoc}
-     *
+     * 

*

For example, if your keys are Git commit hashes:

{@code
      * Map gitCommitMessagesByHash =
      *     ChronicleMapBuilder.of(byte[].class, String.class)
@@ -233,10 +233,10 @@ public class ChronicleMapBuilder implements Cloneable,
      * Configures the optimal number of bytes, taken by serialized form of values, put into maps,
      * created by this builder. If value size is always the same, call {@link
      * #constantValueSizeBySample(Object)} method instead of this one.
-     *
+     * 

*

If value is a boxed primitive type or {@link Byteable} subclass, i. e. if value size is * known statically, it is automatically accounted and shouldn't be specified by user. - * + *

*

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 {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it @@ -258,10 +258,10 @@ public class ChronicleMapBuilder implements Cloneable, * Configures the constant number of bytes, taken by serialized form of values, put into maps, * created by this builder. This is done by providing the {@code sampleValue}, all values should * take the same number of bytes in serialized form, as this sample object. - * + *

*

If values are of boxed primitive type or {@link Byteable} subclass, i. e. if value size is * known statically, it is automatically accounted and this method shouldn't be called. - * + *

*

If value size varies, method {@link #valueSize(int)} or {@link #entrySize(int)} should be * called instead of this one. * @@ -290,16 +290,16 @@ public class ChronicleMapBuilder implements Cloneable, /** * {@inheritDoc} - * + *

*

In fully default case you can expect entry size to be about 256 bytes. But it is strongly * recommended always to configure {@linkplain #keySize(int) key size} and {@linkplain * #valueSize(int) value size}, if they couldn't be derived statically. - * + *

*

If entry size is not configured explicitly by calling this method, it is computed based on * {@linkplain #metaDataBytes(int) 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) alignment}. - * + *

*

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 @@ -347,15 +347,15 @@ public class ChronicleMapBuilder implements Cloneable, /** * Configures alignment strategy of address in memory of entries and independently of address in * memory of values within entries in ChronicleMaps, created by this builder. - * + *

*

Useful when values of the map are updated intensively, particularly fields with 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 {@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 @@ -393,7 +393,8 @@ public class ChronicleMapBuilder implements Cloneable, long actualEntries = entries + (as == 1 ? 1 : // The extra 1 might not be needed. as <= 8 ? entries / 12 : // 6%, (3% was min for tests) - entries / 6); // 16% (8% was min for tests) + as <= 128 ? entries / 7 : // 14% (7% was min for tests) + entries / 5); // 20% (10% was min for tests) // round up to the next multiple of 64. return (int) (Math.max(1, actualEntries / as) + 63) & ~63; @@ -408,8 +409,10 @@ public class ChronicleMapBuilder implements Cloneable, int actualSegments() { if (actualSegments > 0) return actualSegments; - if (!largeSegments && entries > (long) minSegments() << 15) { - long segments = Maths.nextPower2(entries >> 15, 128); + + long entriesWithMostHeadroom = entries + entries / 5; + if (!largeSegments && entriesWithMostHeadroom > (long) minSegments() << 16) { + long segments = Maths.nextPower2(entries * 5 / 6 >> 16, 128); if (segments < 1 << 20) return (int) segments; } @@ -441,12 +444,12 @@ public class ChronicleMapBuilder implements Cloneable, * 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. - * + *

*

{@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. * @@ -468,7 +471,7 @@ public class ChronicleMapBuilder implements Cloneable, /** * 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. - * + *

*

{@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 @@ -629,7 +632,7 @@ public class ChronicleMapBuilder implements Cloneable, /** * {@inheritDoc} - * + *

*

Example:

{@code Map map =
      *     ChronicleMapBuilder.of(Key.class, Value.class)
      *     .entries(1_000_000)
@@ -637,7 +640,7 @@ public class ChronicleMapBuilder implements Cloneable,
      *     // this class hasn't implemented yet, just for example
      *     .objectSerializer(new KryoObjectSerializer())
      *     .create();}
- * + *

*

This serializer is used to serialize both keys and values, if they both require this: * loosely typed, nullable, and custom {@linkplain #keyMarshaller(BytesMarshaller) key} and * {@linkplain #valueMarshallerAndFactory(BytesMarshaller, ObjectFactory) value} marshallers are @@ -652,7 +655,7 @@ public class ChronicleMapBuilder implements Cloneable, /** * {@inheritDoc} - * + *

*

Example: */ @Override @@ -711,10 +714,10 @@ public class ChronicleMapBuilder implements Cloneable, * Specifies the value to be put for each key queried in {@link ChronicleMap#get get()} and * {@link ChronicleMap#getUsing(Object, Object) getUsing()} methods, if the key is absent in the * map. Then this default value is returned from query method. - * + *

*

Setting default value to {@code null} is interpreted as map shouldn't put any default * value for absent keys. This is by default. - * + *

*

This configuration overrides any previous {@link #defaultValueProvider(DefaultValueProvider)} * configuration to this {@code ChronicleMapBuilder}. * @@ -733,7 +736,7 @@ public class ChronicleMapBuilder implements Cloneable, * and {@link ChronicleMap#getUsing(Object, Object) getUsing()} calls, if the key is absent in * the map. If the obtained value is non-null, it is put for the key in the map and then * returned from current {@code get()} or {@code getUsing()} call. - * + *

*

This configuration overrides any previous {@link #defaultValue(Object)} configuration to * this {@code ChronicleMapBuilder}. * diff --git a/src/test/java/net/openhft/chronicle/map/EntryCountMapTest.java b/src/test/java/net/openhft/chronicle/map/EntryCountMapTest.java index ec35eaa29..d51c6f0b7 100644 --- a/src/test/java/net/openhft/chronicle/map/EntryCountMapTest.java +++ b/src/test/java/net/openhft/chronicle/map/EntryCountMapTest.java @@ -1,6 +1,7 @@ package net.openhft.chronicle.map; import net.openhft.lang.values.LongValue; +import org.junit.Ignore; import org.junit.Test; import java.io.File; @@ -90,6 +91,23 @@ public class EntryCountMapTest { System.out.printf("Score: %.2f%n", scoreCount / score); } + @Test + @Ignore + public void testLarge() throws Exception { + int s = 8 * 1024; + for (int i = 0; i < 10; i++) { + System.out.println("i:" + i); + int entries = s * s; + testEntriesMaxSize(s, entries * 4 / 5, entries * 4 / 3, i); +// testEntriesMaxSize(s, entries + 100, entries * 3 / 2, i); + entries *= 2; + testEntriesMaxSize(s, entries * 4 / 5, entries * 4 / 3, i); +// testEntriesMaxSize(s, entries + 100, entries * 3 / 2, i); + } + // hyperbolic average gives more weight to small numbers. + System.out.printf("Score: %.2f%n", scoreCount / score); + } + double score = 0; int scoreCount = 0;