Kotlin运行时性能_

 

 

本文最后更新于:2017年7月12日 凌晨

Kotlin整体的性能相对于Java而言毫不逊色,甚至在一些方面优于Java,本文参考这篇benchmark文章进行Kotlin性能相关总结,关于Kotlin对包大小影响、使用、选择原因等请参考之前的一篇Kotlin的文章,如果对于Java运行时性能感兴趣可以参考这篇文章

前言

根据benchmark文章所有的所有测试均采样200次,使用单位ops/ms(执行次数/毫秒)(因此数值是越大越好)并且均在以下环境:

  • Macbook Pro (2,5 GHz Intel Core i7, 16GB of RAM)
  • Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
  • Kotlin version (1.1.3)
  • JMH (0.5.6)

性能测试结果

1. 性能相比Java更差相关

  • varargs参数展开,Kotlin比Java慢1倍,主要原因是在Kotlin在展开varargs前需要全量拷贝整个数组,这个是非常高的性能开销。
  • Delegated Properties的应用,**Kotlin相比Java慢10%**。

2. 性能相比Java更优相关

  • Lambda的使用,**Kotlin相比Java快30%**,而对用例中的transaction添加inline关键字配置内联后,发现其反而慢了一点点(约1.14%)。
  • Kotlin对companion object的访问相比Java中的静态变量的访问,Kotlin与Java差不多快或更快一点。
  • Kotlin对局部函数(Local Functions)的访问相比Java中的局部函数的访问,Kotlin与Java差不多快或更快一点。
  • Kotlin的非空参数的使用相比没有使用空检查的Java,Kotlin与Java差不多快或更快一点。

3. Kotlin自身比较

  • 对于基本类型范围的使用,无论是否使用常量引用还是直接的范围速度都差不多。
  • 对于非基本类型范围的使用,常量引用相比直接的范围会快3%左右。
  • 对于范围遍历方式中,for循环方式无论有没有使用step速度都差不多,但是如果对范围直接进行.foreach速度会比它们慢3倍,因此避免对范围直接使用.foreach
  • 在遍历中使用lastIndex会比使用indices快2%左右。

实验过程

I. 性能相比Java更差相关

1. varargs参数

测试发现: 对varargs参数展开,Kotlin比Java慢1倍,主要原因是在Kotlin在展开varargs前需要全量拷贝整个数组,这个是非常高的性能开销。

测试用例Kotlin代码:

1
2
3
4
5
6
7
8
9
fun runPrintDouble(blackHole: BlackHole, values: IntArray) {
    printDouble(blackHole, *values)
}

fun printDouble(blackHole: BlackHole, vararg values: Int) {
    for (value in values) {
        blackHole.consume(value)
    }
}
KOTLIN

测试用例Java代码:

1
2
3
4
5
6
7
8
9
public static void runPrintDouble( BlackHole blackHole, int[] values ) {
    printDouble( blackHole, values );
}

public static void printDouble( BlackHole blackHole, int... values ) {
    for (int value : values) {
        blackHole.consume( value );
    }
}
JAVA

测试结果(每毫秒执行次数):

Benchmark平均值平均误差
javaIntVarargs 173265.270 260.837
kotlinIntVarargs 83621.509 990.854

2. Delegated Properties

测试发现:对Delegated Properties的应用,**Kotlin相比Java慢10%**。

测试用例Kotlin代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class StringDelegate {
   private var cache: String? = null

   operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
       var result = cache
       if (result == null) {
           result = someOperation()
           cache = result
       }
       return result!!
   }

   operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
       cache = value
   }
}

class Example {
    var p: String by StringDelegate()
}

fun runStringDelegateExample(blackHole: BlackHole) {
    val example = Example()
    blackHole.consume(example.p)
    blackHole.consume(example.p)
}
KOTLIN

测试用例Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class DelegatePropertyTest {

   public static String stringValue = "hello";

   public static String someOperation() {
       return stringValue;
   }

}

class Example2 {
    public String p;

    public void initialize() {
        p = DelegatePropertyTest.someOperation();
    }
}

public static void runStringDelegateExample( BlackHole blackHole ) {
    Example2 example2 = new Example2();
    example2.initialize();
    blackHole.consume( example2.p );
    blackHole.consume( example2.p );
}
JAVA

测试结果(每毫秒执行次数):

Benchmark平均值平均误差
javaSimplyInitializedProperty 274394.088 554.171
kotlinDelegateProperty 255899.824 910.112

II. 性能相比Java更优相关

1. Lambda

由于Lambda是在Java8中引入,所以对比的是Java8与Kotlin1.1.3

测试发现:对Lambda的使用,**Kotlin相比Java快30%**,而对用例中的transaction添加inline关键字配置内联后,发现其反而慢了一点点(约1.14%)。

测试用例Kotlin代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun transaction(db: Database, body: (Database) -> Int): Int {
    db.beginTransaction()
    try {
        val result = body(db)
        db.setTransactionSuccessful()
        return result
    } finally {
        db.endTransaction()
    }
}

fun kotlinLambda() {
    val deletedRows = transaction(db) {
        it.delete("Customers", null, null)
    }
}
KOTLIN

测试用例Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static int transaction( Database db, ToIntFunction<Database> body ) {
    db.beginTransaction();
    try {
        int result = body.applyAsInt( db );
        db.setTransactionSuccessful();
        return result;
    } finally {
        db.endTransaction();
    }
}

void javaLambda() {
    int deletedRows = transaction( db, ( database ) ->
           database.delete( "Customer", null, null ) );
}
JAVA

测试结果(每毫秒执行次数):

Benchmark平均值平均误差
javaLambda 1024302.409 1851.789
kotlinInlinedFunction 1344885.445 2632.587
kotlinLambda 1362991.121 2824.862

2. 静态(Companion Objects)变量访问

测试发现:Kotlin对companion object的访问相比Java中的静态变量的访问,Kotlin与Java差不多快或更快一点。

测试用例Kotlin代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyClass private constructor() {

    companion object {
        private val TAG = "TAG"

        fun newInstance() = MyClass()
    }

    fun helloWorld() = TAG
}

fun runCompanionObjectCallToPrivateConstructor(): String {
    val myClass = MyClass.newInstance()
    return myClass.helloWorld()
}
KOTLIN

测试用例Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyJavaClass {

    private static final String TAG = "TAG";

    private MyJavaClass() {
    }

    public static String helloWorld() {
        return TAG;
    }

    public static MyJavaClass newInstance() {
        return new MyJavaClass();
    }
}

public static String runPrivateConstructorFromStaticMethod() {
    MyJavaClass myJavaClass = newInstance();
    return myJavaClass.helloWorld();
}
JAVA

测试结果(每毫秒执行次数):

Benchmark平均值平均误差
javaPrivateConstructorCallFromStaticMethod 398709.154 800.190
kotlinPrivateConstructorCallFromCompanionObject 404746.375 621.591

3. 局部函数(Local Functions)访问

测试发现:Kotlin对局部函数的访问相比Java中的局部函数的访问,Kotlin与Java差不多快或更快一点。

测试用例Kotlin代码:

1
2
3
4
5
6
7
8
9
10
11
fun kotlinLocalFunctionCapturingLocalVariable(a: Int): Int {
    fun sumSquare(b: Int) = (a + b) * (a + b)

    return sumSquare(1) + sumSquare(2)
}

fun kotlinLocalFunctionWithoutCapturingLocalVariable(a: Int): Int {
    fun sumSquare(a: Int, b: Int) = (a + b) * (a + b)

    return sumSquare(a, 1) + sumSquare(a, 2)
}
KOTLIN

测试用例Java代码:

1
2
3
4
5
public static int javaLocalFunction( int a ) {
    IntUnaryOperator sumSquare = ( int b ) -> ( a + b ) * ( a + b );

    return sumSquare.applyAsInt( 1 ) + sumSquare.applyAsInt( 2 );
}
JAVA

测试结果(每毫秒执行次数):

Benchmark平均值平均误差
javaLocalFunction 897015.956 1951.104
kotlinLocalFunctionCapturingLocalVariable 909087.356 1690.368
kotlinLocalFunctionWithoutCapturingLocalVariable 908852.870 1822.557

4. 空检查(Null safety)

测试发现:Kotlin的非空参数的使用相比没有使用空检查的Java,Kotlin与Java差不多快或更快一点。

测试用例Kotlin代码:

1
fun sayHello(who: String, blackHole: BlackHole) = blackHole.consume("Hello $who")
KOTLIN

测试用例Java代码:

1
2
3
public static void sayHello( String who, BlackHole blackHole ) {
    blackHole.consume( "Hello " + who );
}
JAVA

测试结果(每毫秒执行次数):

Benchmark平均值平均误差
javaSayHello 73353.725 155.551
kotlinSayHello 75637.556 162.963

III. Kotlin自身比较

1. 基本类型范围

测试发现: 对于基本类型范围的使用,无论是否使用常量引用还是直接的范围速度都差不多。

常量引用基本类型范围用例:

1
2
3
private val myRange get() = 1..10

fun isInOneToTenWithIndirectRange(i: Int) = i in myRange
KOTLIN

直接引用基本类型范围的用例:

1
fun isInOneToTenWithLocalRange(i: Int) = i in 1..10
KOTLIN

测试结果(每毫秒执行次数):

Benchmark平均值平均误差
kotlinIndirectRange 1214464.562 2071.128
kotlinLocallyDeclaredRange 1214883.411 1797.921

2. 非基本类型范围

测试发现: 对于非基本类型范围的使用,常量引用相比直接的范围会快3%左右。

常量引用非基本类型范围用例:

1
2
3
4
5
private val NAMES = "Alfred".."Alicia"

fun isBetweenNamesWithConstantRange(name: String): Boolean {
    return name in NAMES
}
KOTLIN

直接引用非基本类型范围的用例:

1
2
3
fun isBetweenNamesWithLocalRange(name: String): Boolean {
    return name in "Alfred".."Alicia"
}
KOTLIN

测试结果(每毫秒执行次数):

Benchmark平均值平均误差
kotlinStringRangeInclusionWithLocalRange 211468.439 483.879
kotlinStringRangeInclusionWithConstantRange 218073.886 412.408

3. 范围遍历

测试发现: 对于范围遍历方式中,for循环方式无论有没有使用step速度都差不多,但是如果对范围直接进行.foreach速度会比它们慢3倍,因此避免对范围直接使用.foreach

for循环的用例:

1
2
3
4
5
fun rangeForEachLoop(blackHole: BlackHole) {
    for (it in 1..10) {
        blackHole.consume(it)
    }
}
KOTLIN

for循环并且加上step的用例:

1
2
3
4
5
fun rangeForEachLoopWithStep1(blackHole: BlackHole) {
    for (it in 1..10 step 1) {
        blackHole.consume(it)
    }
}
KOTLIN

对范围直接进行.foreach的用例:

1
2
3
4
5
fun rangeForEachMethod(blackHole: BlackHole) {
    (1..10).forEach {
        blackHole.consume(it)
    }
}
KOTLIN

测试结果(每毫秒执行次数):

Benchmark平均值平均误差
kotlinRangeForEachFunction 108382.188 561.632
kotlinRangeForEachLoop 331558.172 494.281
kotlinRangeForEachLoopWithStep1 331250.339 545.200

4. 对于indices对比

测试发现: 使用lastIndex会比使用indices快2%左右。

先创建一个SparseArray:

1
2
3
4
class SparseArray<out T>(val collection: List<T>) {
    fun size() = collection.size
    fun valueAt(index: Int) = collection[index]
}
KOTLIN

使用indices的用例:

1
2
3
4
5
6
7
8
inline val SparseArray<*>.indices: IntRange
    get() = 0..size() - 1

fun printValuesUsingIndices(map: SparseArray<String>, blackHole: BlackHole) {
    for (i in map.indices) {
        blackHole.consume(map.valueAt(i))
    }
}
KOTLIN

使用lastIndex的用例:

1
2
3
4
5
6
7
8
inline val SparseArray<*>.lastIndex: Int
    get() = size() - 1

fun printValuesUsingLastIndexRange(map: SparseArray<String>, blackHole: BlackHole) {
    for (i in 0..map.lastIndex) {
        blackHole.consume(map.valueAt(i))
    }
}
KOTLIN

测试结果(每毫秒执行次数):

Benchmark平均值平均误差
kotlinCustomIndicesIteration 79096.631 134.813
kotlinIterationUsingLastIndexRange 80811.554 122.462



Kotlin运行时性能
https://blog.dreamtobe.cn/kotlin-performance/
https://blog.dreamtobe.cn/kotlin-performance/
posted @   a318013800  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2022-09-14 Build was configured to prefer settings repositories over project repositories but repository ...
2021-09-14 PGrid 蓝牙连接重复类
2019-09-14 树莓派安装最新安卓系统7.0
2019-09-14 下载Android5.1.1源码、编译源码、SDK和内核
2019-09-14 Android系统6.0源代码编译过程分析(2)之编译SDK及内核
2019-09-14 Android系统6.0源代码编译过程分析(1) AAAAAA
2019-09-14 AAAAAA_Android多版本源码及内核编译(Nexus5)
点击右上角即可分享
微信分享提示