Skip to content

Commit 3312ad4

Browse files
committed
Added Android benchmarks module and ported a few benchmarks
1 parent 9731618 commit 3312ad4

File tree

9 files changed

+316
-0
lines changed

9 files changed

+316
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
22+
23+
-dontobfuscate
24+
25+
-ignorewarnings
26+
27+
-keepattributes *Annotation*
28+
29+
-dontnote junit.framework.**
30+
-dontnote junit.runner.**
31+
32+
-dontwarn androidx.test.**
33+
-dontwarn org.junit.**
34+
-dontwarn org.hamcrest.**
35+
-dontwarn com.squareup.javawriter.JavaWriter
36+
37+
-keepclasseswithmembers @org.junit.runner.RunWith public class *

benchmarks-android/build.gradle.kts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
3+
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
4+
*/
5+
plugins {
6+
alias(libs.plugins.android)
7+
alias(libs.plugins.androidx.benchmark)
8+
alias(libs.plugins.kotlin.android)
9+
}
10+
11+
repositories {
12+
mavenCentral()
13+
google()
14+
}
15+
16+
android {
17+
namespace = "kotlinx.io.benchmark.android"
18+
compileSdk = 33
19+
20+
compileOptions {
21+
sourceCompatibility = JavaVersion.VERSION_1_8
22+
targetCompatibility = JavaVersion.VERSION_1_8
23+
}
24+
25+
kotlinOptions {
26+
jvmTarget = "1.8"
27+
}
28+
29+
defaultConfig {
30+
minSdk = 24
31+
32+
testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
33+
testInstrumentationRunnerArguments["androidx.benchmark.output.enable"] = "true"
34+
}
35+
36+
testBuildType = "release"
37+
buildTypes {
38+
debug {
39+
// Since isDebuggable can"t be modified by gradle for library modules,
40+
// it must be done in a manifest - see src/androidTest/AndroidManifest.xml
41+
isMinifyEnabled = true
42+
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "benchmark-proguard-rules.pro")
43+
}
44+
release {
45+
isDefault = true
46+
}
47+
}
48+
}
49+
50+
dependencies {
51+
androidTestImplementation(libs.androidx.test.runner)
52+
androidTestImplementation(libs.androidx.test.junit)
53+
androidTestImplementation(libs.junit4)
54+
androidTestImplementation(libs.androidx.benchmark.junit4)
55+
androidTestImplementation(project(":kotlinx-io-core"))
56+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
4+
~ Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
5+
-->
6+
7+
<manifest
8+
xmlns:android="http://schemas.android.com/apk/res/android"
9+
xmlns:tools="http://schemas.android.com/tools">
10+
11+
<!--
12+
Important: disable debugging for accurate performance results
13+
14+
In a com.android.library project, this flag must be disabled from this
15+
manifest, as it is not possible to override this flag from Gradle.
16+
-->
17+
<application
18+
android:debuggable="false"
19+
tools:ignore="HardcodedDebugMode"
20+
tools:replace="android:debuggable"/>
21+
</manifest>
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
3+
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
4+
*/
5+
6+
package kotlinx.io.benchmark.android
7+
8+
import kotlinx.io.Buffer
9+
import androidx.benchmark.junit4.BenchmarkRule
10+
import androidx.benchmark.junit4.measureRepeated
11+
import androidx.test.ext.junit.runners.AndroidJUnit4
12+
import kotlinx.io.readString
13+
import kotlinx.io.writeString
14+
import org.junit.After
15+
import org.junit.Before
16+
import org.junit.Rule
17+
import org.junit.Test
18+
import org.junit.runner.RunWith
19+
import org.junit.runners.Parameterized
20+
import org.junit.runners.Parameterized.Parameters
21+
22+
abstract class BufferRWBenchmarkBase {
23+
// Buffers are implemented as a list of segments, as soon as a segment is empty
24+
// it will be unlinked. By reading all previously written data, a segment will be
25+
// cleared and recycled, and the next time we will try to write data, a new segment
26+
// will be requested from the pool. Thus, without having any data in-flight, we will
27+
// benchmark not only read/write ops performance, but also segments allocation/reclamation.
28+
// Specific non-zero minGap's values don't significantly affect overall results, but it is
29+
// left as the parameter to allow fine-tuning in the future.
30+
var minGap: Int = 128
31+
32+
protected val buffer = Buffer()
33+
34+
protected open fun padding(): ByteArray = ByteArray(minGap)
35+
36+
@get:Rule
37+
val benchmarkRule = BenchmarkRule()
38+
39+
@Before
40+
fun setupBuffers() {
41+
val padding = padding()
42+
buffer.write(padding)
43+
}
44+
45+
@After
46+
fun clearBuffers() {
47+
buffer.clear()
48+
}
49+
}
50+
51+
@RunWith(AndroidJUnit4::class)
52+
open class IntegerValuesBenchmark : BufferRWBenchmarkBase() {
53+
@Test
54+
fun byteRW() {
55+
benchmarkRule.measureRepeated {
56+
buffer.writeByte(0x42)
57+
buffer.readByte()
58+
}
59+
}
60+
61+
@Test
62+
fun shortRW() {
63+
benchmarkRule.measureRepeated {
64+
buffer.writeShort(0x42)
65+
buffer.readShort()
66+
}
67+
}
68+
69+
@Test
70+
fun intRW() {
71+
benchmarkRule.measureRepeated {
72+
buffer.writeInt(0x42)
73+
buffer.readInt()
74+
}
75+
}
76+
77+
@Test
78+
fun longRW() {
79+
benchmarkRule.measureRepeated {
80+
buffer.writeLong(0x42)
81+
buffer.readLong()
82+
}
83+
}
84+
}
85+
86+
87+
@RunWith(Parameterized::class)
88+
open class Utf8Benchmark(val length: Int, val encoding: String) : BufferRWBenchmarkBase() {
89+
private val strings = mapOf(
90+
"ascii" to ("Um, I'll tell you the problem with the scientific power that you're using here, "
91+
+ "it didn't require any discipline to attain it. You read what others had done and you "
92+
+ "took the next step. You didn't earn the knowledge for yourselves, so you don't take any "
93+
+ "responsibility for it. You stood on the shoulders of geniuses to accomplish something "
94+
+ "as fast as you could, and before you even knew what you had, you patented it, and "
95+
+ "packaged it, and slapped it on a plastic lunchbox, and now you're selling it, you wanna "
96+
+ "sell it."),
97+
"utf8" to
98+
("Սm, I'll 𝓽𝖾ll ᶌօ𝘂 ᴛℎ℮ 𝜚𝕣०bl𝖾m wі𝕥𝒽 𝘵𝘩𝐞 𝓼𝙘𝐢𝔢𝓷𝗍𝜄𝚏𝑖c 𝛠𝝾w𝚎𝑟 𝕥h⍺𝞃 𝛄𝓸𝘂'𝒓𝗲 υ𝖘𝓲𝗇ɡ 𝕙𝚎𝑟e, "
99+
+ "𝛊𝓽 ⅆ𝕚𝐝𝝿'𝗍 𝔯𝙚𝙦ᴜ𝜾𝒓𝘦 𝔞𝘯𝐲 ԁ𝜄𝑠𝚌ι𝘱lι𝒏e 𝑡𝜎 𝕒𝚝𝖙𝓪і𝞹 𝔦𝚝. 𝒀ο𝗎 𝔯𝑒⍺𝖉 w𝐡𝝰𝔱 𝞂𝞽һ𝓮𝓇ƽ հ𝖺𝖉 ⅾ𝛐𝝅ⅇ 𝝰πԁ 𝔂ᴑᴜ 𝓉ﮨ၀𝚔 "
100+
+ "т𝒽𝑒 𝗇𝕖ⅹ𝚝 𝔰𝒕е𝓅. 𝘠ⲟ𝖚 𝖉ⅰԁ𝝕'τ 𝙚𝚊r𝞹 𝘵Ꮒ𝖾 𝝒𝐧هwl𝑒𝖉ƍ𝙚 𝓯૦r 𝔂𝞼𝒖𝕣𝑠𝕖l𝙫𝖊𝓼, 𐑈о y𝘰𝒖 ⅆە𝗇't 𝜏α𝒌𝕖 𝛂𝟉ℽ "
101+
+ "𝐫ⅇ𝗌ⲣ๐ϖ𝖘ꙇᖯ𝓲l𝓲𝒕𝘆 𝐟𝞼𝘳 𝚤𝑡. 𝛶𝛔𝔲 s𝕥σσ𝐝 ﮩ𝕟 𝒕𝗁𝔢 𝘴𝐡𝜎ᴜlⅾ𝓮𝔯𝚜 𝛐𝙛 ᶃ𝚎ᴨᎥս𝚜𝘦𝓈 𝓽𝞸 a𝒄𝚌𝞸mρl𝛊ꜱ𝐡 𝓈𝚘m𝚎𝞃𝔥⍳𝞹𝔤 𝐚𝗌 𝖋a𝐬𝒕 "
102+
+ "αs γ𝛐𝕦 𝔠ﻫ𝛖lԁ, 𝚊π𝑑 Ь𝑒𝙛૦𝓇𝘦 𝓎٥𝖚 ⅇvℯ𝝅 𝜅ո𝒆w w𝗵𝒂𝘁 ᶌ੦𝗎 h𝐚𝗱, 𝜸ﮨ𝒖 𝓹𝝰𝔱𝖾𝗇𝓽𝔢ⅆ і𝕥, 𝚊𝜛𝓭 𝓹𝖺ⅽϰ𝘢ℊеᏧ 𝑖𝞃, "
103+
+ "𝐚𝛑ꓒ 𝙨l𝔞р𝘱𝔢𝓭 ɩ𝗍 ہ𝛑 𝕒 pl𝛂ѕᴛ𝗂𝐜 l𝞄ℼ𝔠𝒽𝑏ﮪ⨯, 𝔞ϖ𝒹 n𝛔w 𝛾𝐨𝞄'𝗿𝔢 ꜱ℮ll𝙞nɡ ɩ𝘁, 𝙮𝕠𝛖 w𝑎ℼ𝚗𝛂 𝕤𝓮ll 𝙞𝓉."),
104+
// The first 't' is actually a '𝓽'
105+
"sparse" to ("Um, I'll 𝓽ell you the problem with the scientific power that you're using here, "
106+
+ "it didn't require any discipline to attain it. You read what others had done and you "
107+
+ "took the next step. You didn't earn the knowledge for yourselves, so you don't take any "
108+
+ "responsibility for it. You stood on the shoulders of geniuses to accomplish something "
109+
+ "as fast as you could, and before you even knew what you had, you patented it, and "
110+
+ "packaged it, and slapped it on a plastic lunchbox, and now you're selling it, you wanna "
111+
+ "sell it."),
112+
"2bytes" to "\u0080\u07ff",
113+
"3bytes" to "\u0800\ud7ff\ue000\uffff",
114+
"4bytes" to "\ud835\udeca",
115+
// high surrogate, 'a', low surrogate, and 'a'
116+
"bad" to "\ud800\u0061\udc00\u0061"
117+
)
118+
119+
private var string: String = ""
120+
121+
public companion object {
122+
@JvmStatic
123+
@Parameters(name = "{0},{1}")
124+
fun data(): Collection<Array<Any>> {
125+
val lengths = listOf(20, 2000, 200000)
126+
val encodings = listOf("ascii", "utf8", "sparse", "2bytes", "3bytes", "4bytes", "bad")
127+
128+
return buildList {
129+
for (l in lengths) {
130+
for (e in encodings) {
131+
add(arrayOf(l, e))
132+
}
133+
}
134+
}
135+
}
136+
}
137+
138+
private fun constructString(): String {
139+
val part = strings[encoding] ?: throw IllegalArgumentException("Unsupported encoding: $encoding")
140+
val builder = StringBuilder(length + 1000)
141+
while (builder.length < length) {
142+
builder.append(part)
143+
}
144+
builder.setLength(length)
145+
return builder.toString()
146+
}
147+
148+
override fun padding(): ByteArray {
149+
val baseString = constructString()
150+
val baseStringByteArray = baseString.encodeToByteArray()
151+
if (baseStringByteArray.size >= minGap) {
152+
return baseStringByteArray
153+
}
154+
val builder = StringBuilder((minGap * 1.5).toInt())
155+
while (builder.length < minGap) {
156+
builder.append(baseString)
157+
}
158+
return builder.toString().encodeToByteArray()
159+
}
160+
161+
@Before
162+
fun setupString() {
163+
string = constructString()
164+
}
165+
166+
@Test
167+
fun readWriteString() {
168+
benchmarkRule.measureRepeated {
169+
val s = buffer.size
170+
buffer.writeString(string)
171+
buffer.readString(buffer.size - s)
172+
}
173+
}
174+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
4+
~ Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
5+
-->
6+
<manifest/>

build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ plugins {
1414
alias(libs.plugins.kover)
1515
alias(libs.plugins.bcv)
1616
alias(libs.plugins.dokka)
17+
18+
alias(libs.plugins.android) apply false
19+
alias(libs.plugins.androidx.benchmark) apply false
20+
alias(libs.plugins.kotlin.android) apply false
1721
}
1822

1923
allprojects {
@@ -47,6 +51,7 @@ subprojects {
4751

4852
apiValidation {
4953
ignoredProjects.add("kotlinx-io-benchmarks")
54+
ignoredProjects.add("kotlinx-io-benchmarks-android")
5055
}
5156

5257
dependencies {

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ kotlin.code.style=official
99
org.gradle.jvmargs=-Xmx4G
1010
nativeBenchmarksEnabled=false
1111
kotlin.mpp.applyDefaultHierarchyTemplate=false
12+
android.useAndroidX=true

gradle/libs.versions.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,30 @@ jmh = "1.36"
99
coroutines = "1.7.3"
1010
animalsniffer = "1.7.1"
1111

12+
agp = "8.1.0"
13+
# Don't update to 1.2.* as it requires SDK version upgrade to 34
14+
androidx-benchmark = "1.1.1"
15+
androidx-test-runner = "1.5.2"
16+
androidx-test-junit = "1.1.5"
17+
junit4 = "4.13.2"
18+
1219
[libraries]
1320

1421
kotlinx-benchmark-runtime = { group = "org.jetbrains.kotlinx", name = "kotlinx-benchmark-runtime", version.ref = "benchmark" }
1522
kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
1623
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
1724
animalsniffer-gradle-plugin = { group = "ru.vyarus", name = "gradle-animalsniffer-plugin", version.ref = "animalsniffer" }
25+
androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidx-test-runner" }
26+
androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" }
27+
junit4 = { group = "junit", name = "junit", version.ref = "junit4" }
28+
androidx-benchmark-junit4 = { group = "androidx.benchmark", name = "benchmark-junit4", version.ref = "androidx-benchmark" }
1829

1930
[plugins]
2031

2132
dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
2233
kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
2334
bcv = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "bcv" }
2435
kotlinx-benchmark-plugin = { id = "org.jetbrains.kotlinx.benchmark", version.ref = "benchmark"}
36+
android = { id = "com.android.library", version.ref = "agp" }
37+
androidx-benchmark = { id = "androidx.benchmark", version.ref = "androidx-benchmark" }
38+
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

settings.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ rootProject.name = "kotlinx-io"
1818
include(":kotlinx-io-core")
1919
include(":kotlinx-io-benchmarks")
2020
include(":kotlinx-io-bytestring")
21+
include(":kotlinx-io-benchmarks-android")
2122
project(":kotlinx-io-core").projectDir = file("./core")
2223
project(":kotlinx-io-benchmarks").projectDir = file("./benchmarks")
2324
project(":kotlinx-io-bytestring").projectDir = file("./bytestring")
25+
project(":kotlinx-io-benchmarks-android").projectDir = file("./benchmarks-android")

0 commit comments

Comments
 (0)