From c90f424e3f643939ff9d768fdaa6b2a89d7e86e5 Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Tue, 11 Nov 2014 00:32:06 +0000 Subject: [PATCH] HCOLL-198 Add a long -> long test. --- .../chronicle/hash/serialization/Hasher.java | 2 + .../internal/LongValueMarshaller.java | 78 +++++++++++++++++++ .../map/AbstractChronicleMapBuilder.java | 11 +-- .../chronicle/map/ChronicleMapTest.java | 76 +++++++++++++++++- 4 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueMarshaller.java diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/Hasher.java b/src/main/java/net/openhft/chronicle/hash/serialization/Hasher.java index 8d1cca2bf..21eeb1d30 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/Hasher.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/Hasher.java @@ -43,6 +43,8 @@ public enum Hasher { } public static long hash(Bytes bytes, long offset, long limit) { + if (limit - offset == 8) + return Maths.hash(bytes.readLong(offset)); long h = 0; long i = offset; for (; i < limit - 7; i += 8) diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueMarshaller.java new file mode 100644 index 000000000..8f9b93bcd --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueMarshaller.java @@ -0,0 +1,78 @@ +/* + * Copyright 2014 Higher Frequency Trading + * + * http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.openhft.chronicle.hash.serialization.internal; + +import net.openhft.chronicle.hash.serialization.BytesInterop; +import net.openhft.chronicle.hash.serialization.BytesReader; +import net.openhft.chronicle.hash.serialization.Hasher; +import net.openhft.chronicle.hash.serialization.SizeMarshaller; +import net.openhft.lang.io.Bytes; +import net.openhft.lang.model.DataValueClasses; +import net.openhft.lang.values.LongValue; + +public enum LongValueMarshaller + implements BytesInterop, BytesReader, SizeMarshaller { + INSTANCE; + + @Override + public long size(LongValue e) { + return 8L; + } + + @Override + public int sizeEncodingSize(long size) { + return 0; + } + + @Override + public void writeSize(Bytes bytes, long size) { + // do nothing + } + + @Override + public boolean startsWith(Bytes bytes, LongValue e) { + return e.getValue() == bytes.readLong(0); + } + + @Override + public long hash(LongValue e) { + return Hasher.hash(e.getValue()); + } + + @Override + public void write(Bytes bytes, LongValue e) { + bytes.writeLong(e.getValue()); + } + + @Override + public long readSize(Bytes bytes) { + return 8L; + } + + @Override + public LongValue read(Bytes bytes, long size) { + return read(bytes, size, DataValueClasses.newInstance(LongValue.class)); + } + + @Override + public LongValue read(Bytes bytes, long size, LongValue toReuse) { + toReuse.setValue(bytes.readLong()); + return toReuse; + } +} diff --git a/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java index 35d57e761..2abcc5b65 100644 --- a/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java @@ -26,7 +26,6 @@ import net.openhft.chronicle.hash.serialization.SizeMarshaller; import net.openhft.chronicle.hash.serialization.internal.MetaBytesInterop; import net.openhft.chronicle.hash.serialization.internal.MetaBytesWriter; import net.openhft.chronicle.hash.serialization.internal.MetaProvider; -import net.openhft.chronicle.map.MultiMapFactory; import net.openhft.chronicle.set.ChronicleSetBuilder; import net.openhft.lang.Maths; import net.openhft.lang.io.ByteBufferBytes; @@ -400,10 +399,12 @@ public abstract class AbstractChronicleMapBuilder 100 ? procs / 2 : procs; + int threads = procs * 3; // runs > 100 ? procs / 2 : procs; 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. @@ -658,6 +658,7 @@ public class ChronicleMapTest { .of(CharSequence.class, LongValue.class) .entries(entries) .entryAndValueAlignment(OF_8_BYTES) + .actualSegments(8 * 1024) .entrySize(24); File tmpFile = File.createTempFile("testAcquirePerf", ".deleteme"); @@ -714,6 +715,79 @@ public class ChronicleMapTest { es.awaitTermination(1, TimeUnit.MINUTES); } + @Test + @Ignore + public void testAcquireLockedLLPerf() + throws IOException, ClassNotFoundException, IllegalAccessException, + InstantiationException, InterruptedException, ExecutionException { +// int runs = Integer.getInteger("runs", 10); + int procs = Runtime.getRuntime().availableProcessors(); + int threads = procs * 3; // runs > 100 ? procs / 2 : procs; + ExecutorService es = Executors.newFixedThreadPool(1); + for (int runs : new int[]{10, 50, 100, 250, 500, 1000, 2500}) { + // JAVA 8 produces more garbage than previous versions for internal work. +// System.gc(); + final long entries = runs * 1000 * 1000L; + OffHeapUpdatableChronicleMapBuilder builder = OffHeapUpdatableChronicleMapBuilder + .of(LongValue.class, LongValue.class) + .entries(entries) + .entryAndValueAlignment(OF_8_BYTES) + .actualSegments(8 * 1024) + .entrySize(16); + + File tmpFile = File.createTempFile("testAcquirePerf", ".deleteme"); + tmpFile.deleteOnExit(); + final ChronicleMap map = builder.createPersistedTo(tmpFile); + + int count = runs > 500 ? runs > 1200 ? 3 : 5 : 5; + final int independence = Math.min(procs, runs > 500 ? 8 : 4); + System.out.println("\nKey size: " + runs + " Million entries. " + builder); + for (int j = 0; j < count; j++) { + long start = System.currentTimeMillis(); + List futures = new ArrayList(); + for (int i = 0; i < threads; i++) { + final int t = i; + futures.add(es.submit(new Runnable() { + @Override + public void run() { + LongValue key = DataValueClasses.newDirectInstance(LongValue.class); + LongValue value = nativeLongValue(); + StringBuilder sb = new StringBuilder(); + long next = 50 * 1000 * 1000; + // use a factor to give up to 10 digit numbers. + int factor = Math.max(1, (int) ((10 * 1000 * 1000 * 1000L - 1) / entries)); + for (long j = t % independence; j < entries + independence - 1; j += independence) { + key.setValue(j * factor); + long n; + try (WriteContext wc = map.acquireUsingLocked(key, value)) { + n = value.addValue(1); + } + assert n > 0 && n < 1000 : "Counter corrupted " + n; + if (t == 0 && j >= next) { + long size = map.longSize(); + if (size < 0) throw new AssertionError("size: " + size); + System.out.println(j + ", size: " + size); + next += 50 * 1000 * 1000; + } + } + } + })); + } + for (Future future : futures) { + future.get(); + } + long time = System.currentTimeMillis() - start; + System.out.printf("Throughput %.1f M ops/sec%n", threads * entries / independence / 1000.0 / time); + } + printStatus(); + File file = map.file(); + map.close(); + file.delete(); + } + es.shutdown(); + es.awaitTermination(1, TimeUnit.MINUTES); + } + @Test @Ignore public void testCHMAcquirePerf() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InterruptedException {