Kotlin / kotlinx-io

Kotlin multiplatform I/O library
Apache License 2.0
1.23k stars 56 forks source link

Leverage VarHandle API #267

Open qwwdfsad opened 7 months ago

qwwdfsad commented 7 months ago

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
fzhinkin commented 7 months ago

@qwwdfsad nice!

qwwdfsad commented 4 months ago

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)

fzhinkin commented 1 month ago

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")
                }
            }
        }
    }