diff --git a/days/build.gradle.kts b/days/build.gradle.kts index e6266c4..6eb33d2 100644 --- a/days/build.gradle.kts +++ b/days/build.gradle.kts @@ -20,5 +20,5 @@ dependencies { } testImplementation(Libs.Jmh.core) - testImplementation(Libs.Jmh.processor) + kaptTest(Libs.Jmh.processor) } diff --git a/days/src/main/kotlin/Day14.kt b/days/src/main/kotlin/Day14.kt index 517d74f..104cbb2 100644 --- a/days/src/main/kotlin/Day14.kt +++ b/days/src/main/kotlin/Day14.kt @@ -6,7 +6,7 @@ import be.vandewalleh.aoc.utils.input.Lines import be.vandewalleh.aoc.utils.input.createDay import kotlin.math.pow import org.eclipse.collections.impl.factory.primitive.IntObjectMaps -import org.eclipse.collections.impl.factory.primitive.ObjectIntMaps +import org.eclipse.collections.impl.factory.primitive.LongIntMaps @Day(14) class Day14(@Lines val input: Input>) { @@ -42,7 +42,7 @@ class Day14(@Lines val input: Input>) { } fun part2(): Long { - val mem = ObjectIntMaps.mutable.empty() + val mem = LongIntMaps.mutable.empty() var currentMask = "" @@ -55,10 +55,10 @@ class Day14(@Lines val input: Input>) { add.toLong().toBin36() to value.toInt() } - val builders = generateMutations(currentMask, address) + val mutations = generateMutations(currentMask, address) - for (builder in builders) { - mem.put(builder.toString(), value) + for (mutation in mutations) { + mem.put(String(mutation).toLong(2), value) } } } @@ -66,9 +66,9 @@ class Day14(@Lines val input: Input>) { return mem.values().sum() } - private fun generateMutations(mask: String, address: String): Array { - val mutations = mask.count { it == 'X' }.let { 2.0.pow(it.toDouble()).toInt() } - val builders = Array(mutations) { StringBuilder(36) } + private fun generateMutations(mask: String, address: String): Array { + val mutationCount = mask.count { it == 'X' }.let { 2.0.pow(it.toDouble()).toInt() } + val mutations = Array(mutationCount) { CharArray(36) } var groups = 1 @@ -76,24 +76,20 @@ class Day14(@Lines val input: Input>) { when (mask[i]) { 'X' -> { groups *= 2 - val groupSize = mutations / groups - var j = 1 - for (b in builders.indices) { - val builder = builders[b] + val groupSize = mutationCount / groups + var currentChar = '1' + for (b in mutations.indices) { + val builder = mutations[b] val flip = b % groupSize == 0 - if (flip) j = if (j == 0) 1 else 0 - builder.append(j) + if (flip) currentChar = if (currentChar == '0') '1' else '0' + builder[i] = currentChar } } - '1' -> { - builders.forEach { it.append('1') } - } - else -> { - builders.forEach { it.append(address[i]) } - } + '1' -> mutations.forEach { it[i] = '1' } + else -> mutations.forEach { it[i] = address[i] } } } - return builders + return mutations } } diff --git a/days/src/test/kotlin/Day14Benchmark.kt b/days/src/test/kotlin/Day14Benchmark.kt new file mode 100644 index 0000000..f6f85b6 --- /dev/null +++ b/days/src/test/kotlin/Day14Benchmark.kt @@ -0,0 +1,112 @@ +package be.vandewalleh.aoc.days + +import java.util.concurrent.TimeUnit +import kotlin.math.pow +import org.openjdk.jmh.annotations.* +import org.openjdk.jmh.infra.Blackhole +import org.openjdk.jmh.runner.Runner +import org.openjdk.jmh.runner.options.Options +import org.openjdk.jmh.runner.options.OptionsBuilder + +/* +Benchmark Mode Cnt Score Error Units +Day14Benchmark.charArrays avgt 5 0.038 ± 0.001 ms/op +Day14Benchmark.countReplacePad avgt 5 0.044 ± 0.002 ms/op +Day14Benchmark.stringBuilders avgt 5 0.082 ± 0.005 ms/op +*/ + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@Warmup(iterations = 3) +@Measurement(iterations = 5) +open class Day14Benchmark { + + private val mask = "1XX10X10100X1010X1000100X0X10X001X00" + private val address = 43398.toString(2).padStart(36, '0') + + @Benchmark + fun stringBuilders(blackhole: Blackhole) { + val mutationCount = mask.count { it == 'X' }.let { 2.0.pow(it.toDouble()).toInt() } + val mutations = Array(mutationCount) { StringBuilder(36) } + + var groups = 1 + + for (i in mask.indices) { + when (mask[i]) { + 'X' -> { + groups *= 2 + val groupSize = mutationCount / groups + var currentChar = '1' + for (b in mutations.indices) { + val builder = mutations[b] + val flip = b % groupSize == 0 + if (flip) currentChar = if (currentChar == '0') '1' else '0' + builder.append(currentChar) + } + } + '1' -> mutations.forEach { it.append('1') } + else -> mutations.forEach { it.append(address[i]) } + } + } + blackhole.consume(mutations) + } + + @Benchmark + fun charArrays(blackhole: Blackhole) { + val mutationCount = mask.count { it == 'X' }.let { 2.0.pow(it.toDouble()).toInt() } + val mutations = Array(mutationCount) { CharArray(36) } + + var groups = 1 + + for (i in mask.indices) { + when (mask[i]) { + 'X' -> { + groups *= 2 + val groupSize = mutationCount / groups + var currentChar = '1' + for (b in mutations.indices) { + val builder = mutations[b] + val flip = b % groupSize == 0 + if (flip) currentChar = if (currentChar == '0') '1' else '0' + builder[i] = currentChar + } + } + '1' -> mutations.forEach { it[i] = '1' } + else -> mutations.forEach { it[i] = address[i] } + } + } + blackhole.consume(mutations) + } + + @Benchmark + fun countReplacePad(blackhole: Blackhole) { + val bitCount = mask.count { it == 'X' } + val mutationCount = bitCount.let { 2.0.pow(it.toDouble()).toInt() } + + val results = Array(mutationCount) { CharArray(36) } + + repeat(mutationCount) { i -> + var count = 0 + val result = results[i] + val currentMask = i.toString(2).padStart(bitCount, '0') + for (j in address.indices) { + result[j] = when (mask[j]) { + 'X' -> currentMask[count++] + '1' -> '1' + else -> address[j] + } + } + } + blackhole.consume(results) + } +} + +fun main() { + val opt: Options = OptionsBuilder() + .include(Day14Benchmark::class.java.simpleName) + .forks(1) + .build() + + Runner(opt).run() +}