diff --git a/calculate_average_ddimtirov.sh b/calculate_average_ddimtirov.sh new file mode 100644 index 0000000..26fb652 --- /dev/null +++ b/calculate_average_ddimtirov.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# +# 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. +# + + +JAVA_OPTS="-XX:+UseParallelGC" +time java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_ddimtirov diff --git a/src/main/java/dev/morling/onebrc/CalculateAverage_ddimtirov.java b/src/main/java/dev/morling/onebrc/CalculateAverage_ddimtirov.java new file mode 100644 index 0000000..285ab00 --- /dev/null +++ b/src/main/java/dev/morling/onebrc/CalculateAverage_ddimtirov.java @@ -0,0 +1,131 @@ +/* + * 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. + */ +package dev.morling.onebrc; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.time.Instant; +import java.util.TreeMap; +import java.util.stream.Collector; + +import static java.util.stream.Collectors.groupingByConcurrent; + +// gunnar morling - 2:10 +// roy van rijn - 1:01 +// 0:53 + +public class CalculateAverage_ddimtirov { + public record InputLine(String station, int value) { + + public static InputLine fromLine(String line) { + int endOfText = line.indexOf(";"); + + String station = line.substring(0, endOfText); + + int startOfWhole = endOfText + 1; + int sign; + if (line.charAt(startOfWhole) == '-') { + sign = -1; + startOfWhole++; + } else { + sign = 1; + } + + int endOfWhole = line.lastIndexOf("."); + var whole = unsafeParsePositiveInt(line, startOfWhole, endOfWhole); + var decimal = unsafeParsePositiveInt(line,endOfWhole+1, line.length()); + int fixpoint10 = (whole * 10 + decimal) * sign; + + return new InputLine(station, fixpoint10); + } + + static int unsafeParsePositiveInt(String s, int start, int end) { + int acc = 0; + for (int i = start; i=0 && v<=9 : String.format("Character '%s', value %,d", c, v); + acc += v; + } + return acc; + } + } + + private static class OutputMetrics { + private int min = Integer.MAX_VALUE; + private int max = Integer.MIN_VALUE; + private long sum; + private long count; + + @SuppressWarnings("ManualMinMaxCalculation") + public OutputMetrics combine(OutputMetrics o) { + var r = new OutputMetrics(); + r.min = min < o.min ? min : o.min; + r.max = max > o.max ? max : o.max; + r.sum = sum + o.sum; + r.count = count + o.count; + return r; + } + + public void accumulate(InputLine m) { + if (m.value < min) min = m.value; + if (m.value > max) max = m.value; + sum += m.value; + count++; + } + + @Override + public String toString() { + var min = this.min / 10.0; + var mean = Math.round(this.sum / (double) count) / 10.0; + var max = this.max / 10.0; + return min + "/" + mean + "/" + max; + } + } + + public static void main(String[] args) throws IOException { +// var start = Instant.now(); + Instant start = null; + var path = Path.of("./measurements.txt"); + var buffer = 8192 * 20; + + // Files.lines() is optimized for files that can be indexed by int + // For larger files it falls back to buffered reader, which we now + // use directly to be able to tweak the buffer size. + try (var reader = new BufferedReader(new InputStreamReader(Files.newInputStream(path)), buffer)) { + var stationsToMetrics = reader.lines() + .map(InputLine::fromLine) + .parallel() + .collect(groupingByConcurrent(InputLine::station, Collector.of( + OutputMetrics::new, + OutputMetrics::accumulate, + OutputMetrics::combine, + OutputMetrics::toString + ))); + System.out.println(new TreeMap<>(stationsToMetrics)); + assert Files.readAllLines(Path.of("expected_result.txt")).getFirst().equals(new TreeMap<>(stationsToMetrics).toString()); + } + + //noinspection ConstantValue + if (start!=null) System.err.println(Duration.between(start, Instant.now())); + } + +}