Skip to content

Commit

Permalink
Solutions | Added Solution & Benchmark Class Kotlin Doc
Browse files Browse the repository at this point in the history
  • Loading branch information
TomPlum committed Jan 3, 2021
1 parent 55fa1b2 commit 7918775
Show file tree
Hide file tree
Showing 15 changed files with 140 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.tomplum.libs.solutions
import io.github.tomplum.libs.solutions.benchmark.data.Benchmark
import io.github.tomplum.libs.solutions.benchmark.data.BenchmarkResult
import io.github.tomplum.libs.solutions.benchmark.utility.BenchmarkUtility
import io.github.tomplum.libs.solutions.benchmark.report.BenchmarkReport
import io.github.tomplum.libs.logging.AdventLogger
import kotlin.system.measureNanoTime

Expand All @@ -12,6 +13,11 @@ import kotlin.system.measureNanoTime
*/
class SolutionRunner private constructor() {
companion object {
/**
* Runs the given [solutions], measures their runtime and print a [BenchmarkReport]
* to the console.
* @param solutions The solutions to run.
*/
fun run(vararg solutions: Solution<*, *>) {
val result = BenchmarkResult()

Expand All @@ -25,6 +31,12 @@ class SolutionRunner private constructor() {
BenchmarkUtility().log(result)
}

/**
* Runs the chosen [part] for the given [solution] and measures its runtime in nanoseconds.
* @param solution The solution.
* @param part The part to run.
* @return The answer returned by the solution and its runtime.
*/
private fun runPart(solution: Solution<*, *>, part: Part): Answer {
var answer: Any?
val runtime = measureNanoTime {
Expand All @@ -36,6 +48,11 @@ class SolutionRunner private constructor() {
return Answer(answer, runtime)
}

/**
* Extracts the day number from the given solution class.
* Supports only 1 or 2 digit day numbers.
* @return The day number of the given solution.
*/
private fun Solution<*, *>.dayNumber(): Int {
val lastTwo = this.javaClass.simpleName.takeLast(2)
return if (lastTwo.all { it.isDigit() }) lastTwo.toInt() else lastTwo.takeLast(1).toInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ package io.github.tomplum.libs.solutions.benchmark.data
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty

/**
* A benchmark for a single Advent of Code day.
*
* @param day The day number. 1 or 2 digits.
* @param answer1 The answer to part 1 of the [day].
* @param answer2 The answer to part 2 of the [day].
* @param runtime1 The measured runtime of part 1 in nanoseconds.
* @param runtime2 The measured runtime of part 2 in nanoseconds.
*/
class Benchmark<T> @JsonCreator constructor(
@JsonProperty("day") val day: Int,
@JsonProperty("answer1") val answer1: T,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
package io.github.tomplum.libs.solutions.benchmark.data

/**
* A comparison between two [BenchmarkResult].
* Calculates runtime deltas between the [previousRun] and the [lastRun].
* @param previousRun The previous solution run from the serialised XML file.
* @param lastRun The most recent solution to run.
*/
class BenchmarkComparison(private val previousRun: BenchmarkResult, val lastRun: BenchmarkResult) {
/**
* Calculates the runtime delta between each of the respective day parts for given runs.
* @return A list of benchmark result runtime deltas.
*/
fun getDeltas(): List<BenchmarkDelta> = previousRun.results.zip(lastRun.results).map { (previous, last) ->
BenchmarkDelta(previous.day, last.runtime1 - previous.runtime1, last.runtime2 - previous.runtime2)
}

/**
* Calculates the runtime delta between the average runtime of both results.
* @return The delta of the runtime averages.
*/
fun getAvgDelta(): Long = lastRun.getAvgTime() - previousRun.getAvgTime()

/**
* Calculates the runtime delta between the total runtime of both results.
* @return The delta of the runtime totals.
*/
fun getTotalDelta(): Long = lastRun.getTotalTime() - previousRun.getTotalTime()
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
package io.github.tomplum.libs.solutions.benchmark.data

/**
* The runtime deltas of a single days solution between two benchmarking runs.
*
* @param day The day number.
* @param p1 The runtime delta of part one in nanoseconds.
* @param p2 The runtime delta of part two in nanoseconds.
*/
data class BenchmarkDelta(val day: Int, val p1: Long, val p2: Long)
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,46 @@ package io.github.tomplum.libs.solutions.benchmark.data

import com.fasterxml.jackson.annotation.JsonIgnore

/**
* The result of a benchmark run for n number of solutions.
* @property results A list of benchmark results.
*/
class BenchmarkResult {
val results = mutableListOf<Benchmark<*>>()

/**
* Adds a single [benchmark] to the result.
* @param benchmark The benchmark to add.
*/
fun add(benchmark: Benchmark<*>) = results.add(benchmark)

fun get(day: Int): Benchmark<*> = results.find { it.day == day } ?: throw IllegalArgumentException("No Benchmark Result For Day $day")
/**
* Gets a single benchmark from the result with the given [day] number.
* @param day The number of the day. 1 or 2 digits.
* @throws IllegalArgumentException if there is no benchmark result for the given day.
* @return The benchmark for the given day.
*/
fun get(day: Int): Benchmark<*> = results.find { benchmark ->
benchmark.day == day
} ?: throw IllegalArgumentException("No Benchmark Result For Day $day")

/**
* Calculates the total runtime of all the benchmark [results].
* @return The runtime sum in nanoseconds.
*/
@JsonIgnore
fun getTotalTime(): Long = results.map { it.runtime1 + it.runtime2 }.sum()
fun getTotalTime(): Long = sumDayParts().sum()

/**
* Calculates the average runtime of all the benchmark [results].
* @return The runtime sum in nanoseconds.
*/
@JsonIgnore
fun getAvgTime(): Long = results.map { it.runtime1 + it.runtime2 }.average().toLong()
fun getAvgTime(): Long = sumDayParts().average().toLong()

/**
* Iterates over the [results] and sums both parts together for each day.
* @return A list of the sums of both day parts.
*/
private fun sumDayParts() = results.map { benchmark -> benchmark.runtime1 + benchmark.runtime2 }
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ const val reset = "\u001B[0m"

fun String.coloured(colour: Colour) = "${colour.escapeCode}$this$reset"

/**
* An ANSI escape code for coloured printing in benchmark reports.
* @param escapeCode The escape code for the colour.
*/
enum class Colour(val escapeCode: String) {
GREEN("\u001B[32m"),
RED("\u001B[31m")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ package io.github.tomplum.libs.solutions.benchmark.report

import io.github.tomplum.libs.solutions.benchmark.data.BenchmarkComparison

/**
* A more succinct variant of [DeltaReport].
* Still reports the same information as [BenchmarkComparisonReport] but omits
* all the unnecessary line breaks.
* @param comparison The benchmark result to format.
*/
class BenchmarkCompactReport(private val comparison: BenchmarkComparison) : DeltaReport() {

override fun toString(): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ package io.github.tomplum.libs.solutions.benchmark.report

import io.github.tomplum.libs.solutions.benchmark.data.BenchmarkComparison

/**
* The default verbose variant of [DeltaReport].
* @see BenchmarkCompactReport for a more succinct variant.
* @param comparison The benchmark result to format.
*/
class BenchmarkComparisonReport(private val comparison: BenchmarkComparison) : DeltaReport() {

override fun toString(): String {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package io.github.tomplum.libs.solutions.benchmark.report

import io.github.tomplum.libs.solutions.benchmark.data.BenchmarkResult
import io.github.tomplum.libs.solutions.benchmark.utility.BenchmarkReader

/**
* The default [BenchmarkReport] that is used to format the [BenchmarkResult] when
* the [BenchmarkReader] cannot find an existing XML result from a previous run.
* Simply reports the solutions from the current [result] as runtime deltas are not possible.
* @param result The benchmark result to format.
*/
class BenchmarkDefaultReport(private val result: BenchmarkResult) : BenchmarkReport() {

override fun toString(): String {
val s = StringBuilder("- Advent of Code 2020 Solution Report -\n\n")
val solutions = result.results.joinToString("\n") { result ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package io.github.tomplum.libs.solutions.benchmark.report

import kotlin.math.abs
import io.github.tomplum.libs.solutions.SolutionRunner

/**
* A report of the benchmark results from the [SolutionRunner].
*/
abstract class BenchmarkReport {
/**
* Formats the given [time] as a presentable string with units.
Expand All @@ -15,6 +19,7 @@ abstract class BenchmarkReport {
val remainingNanos = time % 1_000_000_000
val remainingMillis = remainingNanos / 1_000_000
val micro = time / 1000

return when {
abs(s) > 0 -> "${s}s ${remainingMillis}ms"
abs(ms) >= 1 -> "${ms}ms"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package io.github.tomplum.libs.solutions.benchmark.report

import io.github.tomplum.libs.solutions.benchmark.report.BenchmarkReport
import io.github.tomplum.libs.solutions.benchmark.report.Colour
import io.github.tomplum.libs.solutions.benchmark.report.coloured

/**
* A variant of [BenchmarkReport] that is produced when there is a recorded previous run.
* Reports the runtime deltas between each of the days parts and the total/average times.
*/
abstract class DeltaReport : BenchmarkReport() {
protected fun formatExecutionTime(runtime: Long, delta: Long): String {
return "${formatTime(runtime)} (${formatDelta(delta)})\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package io.github.tomplum.libs.solutions.benchmark.report

import io.github.tomplum.libs.solutions.benchmark.utility.BenchmarkUtility

/**
* Denotes the type of benchmark report to be produced.
* @see BenchmarkUtility
*/
enum class ReportingMode {
COMPACT, VERBOSE, DEFAULT
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import io.github.tomplum.libs.logging.AdventLogger
import java.io.FileNotFoundException
import java.io.FileReader

/**
* Reads an existing [BenchmarkResult] from it's serialised XML file.
*/
class BenchmarkReader {
/**
* Looks for an existing 'benchmark.xml' file and maps it to a [BenchmarkResult].
* @return The previous benchmark result or null if doesn't exist.
*/
fun read(): BenchmarkResult? {
val reader = try {
FileReader("benchmark.xml")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ class BenchmarkUtility {
private val reader = BenchmarkReader()
private val writer = BenchmarkWriter()

fun log(lastRun: BenchmarkResult) {
fun log(currentRun: BenchmarkResult) {
val previousRun = reader.read()
if (previousRun != null) {
val comparison = BenchmarkComparison(previousRun, lastRun)
val comparison = BenchmarkComparison(previousRun, currentRun)
val mode = when(System.getProperty("report")) {
"verbose" -> ReportingMode.VERBOSE
"compact" -> ReportingMode.COMPACT
Expand All @@ -29,8 +29,8 @@ class BenchmarkUtility {
}

} else {
AdventLogger.error(BenchmarkDefaultReport(lastRun))
AdventLogger.error(BenchmarkDefaultReport(currentRun))
}
writer.write(lastRun)
writer.write(currentRun)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ import com.fasterxml.jackson.dataformat.xml.XmlMapper
import io.github.tomplum.libs.solutions.benchmark.data.BenchmarkResult
import java.io.File

/**
* Writes a [BenchmarkResult] to an XML file.
*/
class BenchmarkWriter {
/**
* Writes the given [result] to a 'benchmark.xml' file.
* @param result The result to serialise.
*/
fun write(result: BenchmarkResult) {
val mapper = XmlMapper()
mapper.enable(SerializationFeature.INDENT_OUTPUT)
Expand Down

0 comments on commit 7918775

Please sign in to comment.