From d733b63d6d662e76fca6ea377268cf3a8182c073 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 21 Aug 2024 13:55:58 +0200 Subject: [PATCH 1/2] Minor cleanup and simplification of readString * Request Long.MAX_VALUE from the source at once (note: no need for a loop as we are unable to build the string of such size anyway) * Get rid of redundant checks in commonReadUtf8 Ticks one of the boxes in #342 --- benchmarks/README.md | 11 ++++- .../commonMain/kotlin/ReadStringBenchmark.kt | 42 +++++++++++++++++++ core/common/src/Utf8.kt | 17 +++----- 3 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt diff --git a/benchmarks/README.md b/benchmarks/README.md index 49ba39a0d..dc2917d35 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -8,4 +8,13 @@ Currently, the suite includes benchmarks on: - segment pooling performance. The suite doesn't include benchmarks for more complex APIs inherited from Okio as these APIs are subject to change. -Such benchmarks will be added later along with corresponding changes in the library. \ No newline at end of file +Such benchmarks will be added later along with corresponding changes in the library. + +### Quickstart + +For JVM: +``` +./gradlew :kotlinx-io-benchmarks:jvmJar + +java -jar benchmarks/build/benchmarks/jvm/jars/kotlinx-io-benchmarks-jvm-jmh-0.6.0-SNAPSHOT-JMH.jar ReadStringBenchmark -f 1 -wi 5 -i 5 -tu us -w 1 -r 1 +``` \ No newline at end of file diff --git a/benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt b/benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt new file mode 100644 index 000000000..694f6bf1f --- /dev/null +++ b/benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt @@ -0,0 +1,42 @@ +package kotlinx.io.benchmarks + +import kotlinx.benchmark.Benchmark +import kotlinx.benchmark.Param +import kotlinx.benchmark.Scope +import kotlinx.benchmark.Setup +import kotlinx.benchmark.State +import kotlinx.io.Buffer +import kotlinx.io.Source +import kotlinx.io.readCodePointValue +import kotlinx.io.readString +import kotlinx.io.writeCodePointValue +import kotlinx.io.writeString +import kotlin.random.Random + + +@State(Scope.Benchmark) +open class ReadStringBenchmark() { + + @Param("16", "64", "512") + var size: Int = 0 + + val buffer: Buffer = Buffer() + + @Setup + fun setup() { + val string = buildString { repeat(size) { append(('a'..'z').random()) } } + buffer.writeString(string) + } + + + @Benchmark + fun bufferReadString(): String { + return buffer.copy().readString() + } + + @Benchmark + fun sourceReadString(): String { + val source: Source = buffer.copy() + return source.readString() + } +} diff --git a/core/common/src/Utf8.kt b/core/common/src/Utf8.kt index d4e7202d5..f455fbce3 100644 --- a/core/common/src/Utf8.kt +++ b/core/common/src/Utf8.kt @@ -206,10 +206,7 @@ public fun Sink.writeString(chars: CharSequence, startIndex: Int = 0, endIndex: */ @OptIn(InternalIoApi::class) public fun Source.readString(): String { - var req: Long = Segment.SIZE.toLong() - while (request(req)) { - req *= 2 - } + request(Long.MAX_VALUE) // Request all data return buffer.commonReadUtf8(buffer.size) } @@ -607,10 +604,7 @@ private fun Buffer.commonWriteUtf8CodePoint(codePoint: Int) { @OptIn(UnsafeIoApi::class) private fun Buffer.commonReadUtf8(byteCount: Long): String { - require(byteCount >= 0 && byteCount <= Int.MAX_VALUE) { - "byteCount ($byteCount) is not within the range [0..${Int.MAX_VALUE})" - } - require(byteCount) + // Invariant: byteCount was request()'ed into this buffer beforehand if (byteCount == 0L) return "" UnsafeBufferOperations.iterate(this) { ctx, head -> @@ -624,7 +618,6 @@ private fun Buffer.commonReadUtf8(byteCount: Long): String { } } } - - // If the string spans multiple segments, delegate to readBytes(). - return readByteArray(byteCount.toInt()).commonToUtf8String() -} + // If the string spans multiple segments, delegate to readBytes() + return readByteArray(byteCount.toInt()).commonToUtf8String() + } From 49e16cc408c6564d7b5100a4ff34140fe9a0803a Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Fri, 23 Aug 2024 16:30:24 +0200 Subject: [PATCH 2/2] ~comment --- benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt b/benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt index 694f6bf1f..1b1d15b98 100644 --- a/benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt +++ b/benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt @@ -17,7 +17,7 @@ import kotlin.random.Random @State(Scope.Benchmark) open class ReadStringBenchmark() { - @Param("16", "64", "512") + @Param("16", "64", "512") // Fits into a single segment, so the benchmark does not measure segment boundaries crossing var size: Int = 0 val buffer: Buffer = Buffer()