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..1b1d15b98 --- /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") // Fits into a single segment, so the benchmark does not measure segment boundaries crossing + 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() + }