kotlinx-io
kotlinx-io copied to clipboard
Leverage VarHandle API
Currently, all operations in Buffer operate with byte[] abstraction, meaning that any compound operation incurs overhead from reading and combining individual bytes.
Apart from (potentially non-optimizable) overhead, such an implementation also limits us from providing more efficient primitives -- e.g. SWAR-based indexOf.
It's worth considering VarHandles API for all our bulk operations that operate with anything bigger than byte, ideally isolated through a clear (internal) API boundary via a multi-release jar.
Here is my experiment from qwwdfsad/varhandles that gives a taste of a potential performance difference:
@Benchmark
fun write(): Buffer {
val buffer = Buffer()
for (long in data) {
buffer.writeLong(long)
}
return buffer
}
@Benchmark
fun writeVh(): Buffer {
val buffer = Buffer()
for (long in data) {
buffer.writeLongVh(long)
}
return buffer
}
@Benchmark
fun readLongArray(): LongArray {
val copy = buffer.copy()
for (index in data.indices) {
data[index] = copy.readLong()
}
return data
}
@Benchmark
fun readLongArrayVh(): LongArray {
val copy = buffer.copy()
for (index in data.indices) {
data[index] = copy.readLongVh()
}
return data
}
BulkWriteBenchmark.readLongArray avgt 5 329.162 ± 20.283 us/op
BulkWriteBenchmark.readLongArrayVh avgt 5 272.699 ± 7.798 us/op
BulkWriteBenchmark.write avgt 5 360.114 ± 26.572 us/op
BulkWriteBenchmark.writeVh avgt 5 241.212 ± 75.791 us/op
@qwwdfsad nice!
It also opens a road to more advanced parsing capabilities, for example the most recent one: https://github.com/eclipse-vertx/vertx-sql-client/blob/cf176f47c270cffb04df4e47639da440450d4a0e/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/codec/CommonCodec.java (credit to https://twitter.com/forked_franz/status/1783832904344506820)
Apparently, building multi-release jars is as easy as adding following config into the core's build script:
jvm {
compilations {
val main by getting
val java17 by creating {
associateWith(main)
defaultSourceSet {
kotlin.srcDir("jvm/src17/")
}
compileTaskProvider.configure {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
compileJavaTaskProvider?.configure {
options.release.set(17)
}
}
tasks.withType(Jar::class.java).configureEach {
from(java17.output) {
into("META-INF/versions/17")
}
manifest {
attributes("Multi-Release" to "true")
}
}
}
}