diff --git a/calculate_average_iziamos.sh b/calculate_average_iziamos.sh index 755dddc77..0f9178797 100755 --- a/calculate_average_iziamos.sh +++ b/calculate_average_iziamos.sh @@ -15,11 +15,18 @@ # limitations under the License. # -JAVA_OPTS="--enable-preview - -XX:+UnlockExperimentalVMOptions \ - -XX:+UseEpsilonGC -Xms16m -Xmx16m -XX:-AlwaysPreTouch \ - -XX:-TieredCompilation -XX:CICompilerCount=1 -XX:CompilationMode=high-only \ - -XX:C1MaxTrivialSize=500 -XX:-UseCountedLoopSafepoints -XX:+UseCMoveUnconditionally -XX:+DisableAttachMechanism \ - -XX:-PreserveFramePointer -Xnoclassgc -disablesystemassertions -XX:-UsePerfData \ - -XX:-UseTransparentHugePages -XX:-UseCompressedOops" -java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_iziamos + +if [ -f target/CalculateAverage_iziamos_image ]; then + echo "Using graal" 1>&2 + target/CalculateAverage_iziamos_image +else + echo "Using openjdk" 1>&2 + JAVA_OPTS="--enable-preview + -XX:+UnlockExperimentalVMOptions \ + -XX:+UseEpsilonGC -Xms16m -Xmx16m -XX:-AlwaysPreTouch \ + -XX:-TieredCompilation -XX:CICompilerCount=1 -XX:CompilationMode=high-only \ + -XX:C1MaxTrivialSize=500 -XX:-UseCountedLoopSafepoints -XX:+UseCMoveUnconditionally -XX:+DisableAttachMechanism \ + -XX:-PreserveFramePointer -Xnoclassgc -disablesystemassertions -XX:-UsePerfData \ + -XX:-UseTransparentHugePages -XX:-UseCompressedOops" + java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_iziamos +fi diff --git a/prepare_iziamos.sh b/prepare_iziamos.sh new file mode 100755 index 000000000..621937ca2 --- /dev/null +++ b/prepare_iziamos.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright 2023 The original authors +# +# 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. +# + +source "$HOME/.sdkman/bin/sdkman-init.sh" +sdk use java 21.0.2-graal 1>&2 + +if [ ! -f target/CalculateAverage_iziamos_image ]; then + NATIVE_IMAGE_OPTS="-H:+UnlockExperimentalVMOptions --gc=epsilon -O3 -march=native -R:MaxHeapSize=64m -H:-GenLoopSafepoints --enable-preview -H:InlineAllBonus=10 -H:-ParseRuntimeOptions --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_iziamos" + native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_iziamos_image dev.morling.onebrc.CalculateAverage_iziamos +fi diff --git a/src/main/java/dev/morling/onebrc/CalculateAverage_iziamos.java b/src/main/java/dev/morling/onebrc/CalculateAverage_iziamos.java index faa60547d..f4ca68f36 100644 --- a/src/main/java/dev/morling/onebrc/CalculateAverage_iziamos.java +++ b/src/main/java/dev/morling/onebrc/CalculateAverage_iziamos.java @@ -15,12 +15,9 @@ */ package dev.morling.onebrc; -import sun.misc.Unsafe; - import java.io.IOException; +import java.io.UncheckedIOException; import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; -import java.lang.reflect.Field; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.file.Files; @@ -34,45 +31,42 @@ import static java.nio.file.StandardOpenOption.READ; public class CalculateAverage_iziamos { - private static final Unsafe UNSAFE; + private static final sun.misc.Unsafe UNSAFE = initUnsafe(); - private static final String FILE = "./measurements.txt"; - private static final Arena GLOBAL_ARENA = Arena.global(); - private final static MemorySegment WHOLE_FILE_SEGMENT; - private final static long FILE_SIZE; - private final static long BASE_POINTER; - private final static long END_POINTER; - - static { + private static sun.misc.Unsafe initUnsafe() { try { - final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + java.lang.reflect.Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); - UNSAFE = (Unsafe) theUnsafe.get(Unsafe.class); - - final var fileChannel = (FileChannel) Files.newByteChannel(Path.of(FILE), READ); - WHOLE_FILE_SEGMENT = fileChannel.map(READ_ONLY, 0, fileChannel.size(), GLOBAL_ARENA); - + return (sun.misc.Unsafe) theUnsafe.get(sun.misc.Unsafe.class); } - catch (final NoSuchFieldException | IllegalAccessException | IOException e) { + catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } - - FILE_SIZE = WHOLE_FILE_SEGMENT.byteSize(); - BASE_POINTER = WHOLE_FILE_SEGMENT.address(); - END_POINTER = BASE_POINTER + FILE_SIZE; } - private static final long CHUNK_SIZE = 64 * 1024 * 1024; - // private static final long CHUNK_SIZE = Long.MAX_VALUE; + private static final String FILE = "./measurements.txt"; + private static final Arena GLOBAL_ARENA = Arena.global(); public static void main(String[] args) throws Exception { - // Thread.sleep(10_000); + // final long chunkSize = Long.MAX_VALUE; + final long chunkSize = 64 * 1024 * 1024; + + final FileChannel fileChannel; + try { + fileChannel = (FileChannel) Files.newByteChannel(Path.of(FILE), READ); + } + catch (final IOException e) { + throw new UncheckedIOException(e); + } - final long threadCount = 1 + FILE_SIZE / CHUNK_SIZE; + final var seg = fileChannel.map(READ_ONLY, 0, fileChannel.size(), GLOBAL_ARENA); + + final long fileSize = seg.byteSize(); + final long threadCount = 1 + fileSize / chunkSize; final var processingFutures = new CompletableFuture[(int) threadCount]; for (int i = 0; i < threadCount; ++i) { - processingFutures[i] = processSegment(i, CHUNK_SIZE); + processingFutures[i] = processSegment(seg.address(), seg.address() + fileSize, i, chunkSize); } final long aggregate = (long) processingFutures[0].get(); @@ -102,15 +96,18 @@ private double round(double value) { } } - private static CompletableFuture processSegment(final long chunkNumber, final long chunkSize) { + private static CompletableFuture processSegment(final long basePointer, + final long endPointer, + final long chunkNumber, + final long chunkSize) { final var ret = new CompletableFuture(); Thread.ofVirtual().start(() -> { final long relativeStart = chunkNumber * chunkSize; - final long absoluteStart = BASE_POINTER + relativeStart; + final long absoluteStart = basePointer + relativeStart; - final long absoluteEnd = computeAbsoluteEndWithSlack(absoluteStart + chunkSize); - final long startOffsetAfterSkipping = skipIncomplete(WHOLE_FILE_SEGMENT.address(), absoluteStart); + final long absoluteEnd = computeAbsoluteEndWithSlack(absoluteStart + chunkSize, endPointer); + final long startOffsetAfterSkipping = skipIncomplete(basePointer, absoluteStart); final long result = processEvents(startOffsetAfterSkipping, absoluteEnd); ret.complete(result); @@ -119,8 +116,8 @@ private static CompletableFuture processSegment(final long chunkNumber, fi return ret; } - private static long computeAbsoluteEndWithSlack(final long chunk) { - return Long.compareUnsigned(END_POINTER, chunk) > 0 ? chunk : END_POINTER; + private static long computeAbsoluteEndWithSlack(final long chunk, final long endPointer) { + return Long.compareUnsigned(endPointer, chunk) > 0 ? chunk : endPointer; } private static long skipIncomplete(final long basePointer, final long start) { @@ -142,7 +139,7 @@ private static long processEvents(final long start, final long limit) { } private static void scalarLoop(final long start, final long limit, final long result) { - final LoopCursor cursor = new ScalarLoopCursor(start, limit); + final LoopCursor cursor = new LoopCursor(start, limit); while (cursor.hasMore()) { final long address = cursor.getCurrentAddress(); final int length = cursor.getStringLength(); @@ -152,25 +149,13 @@ private static void scalarLoop(final long start, final long limit, final long re } } - public interface LoopCursor { - long getCurrentAddress(); - - int getStringLength(); - - int getHash(); - - int getCurrentValue(); - - boolean hasMore(); - } - - public static class ScalarLoopCursor implements LoopCursor { + public static class LoopCursor { private long pointer; private final long limit; private int hash = 0; - public ScalarLoopCursor(final long pointer, final long limit) { + public LoopCursor(final long pointer, final long limit) { this.pointer = pointer; this.limit = limit; }