Skip to content

Commit

Permalink
Minor cleanup and simplification of readString (#376)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
qwwdfsad authored Aug 23, 2024
1 parent 8a86a68 commit 92e0214
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 13 deletions.
11 changes: 10 additions & 1 deletion benchmarks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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
```
42 changes: 42 additions & 0 deletions benchmarks/src/commonMain/kotlin/ReadStringBenchmark.kt
Original file line number Diff line number Diff line change
@@ -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()
}
}
17 changes: 5 additions & 12 deletions core/common/src/Utf8.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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 ->
Expand All @@ -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()
}

0 comments on commit 92e0214

Please sign in to comment.