본문 바로가기
프로그래밍 놀이터/Kotlin, Coroutine

[Kotlin] Kotlin 의 숨겨진 비용 #2

by 돼지왕 왕돼지 2018. 1. 17.
반응형

 [Kotlin] Kotlin 의 숨겨진 비용 #2


https://medium.com/@BladeCoder/exploring-kotlins-hidden-costs-part-2-324a4a50b70


*, about array, array, assumenosideeffects, Boxing, Builder, Capture, checkparameterisnotnull, Compile, compile null safe, compiler option, copied, function, function object, generic synthetic method, IllegalArgumentException, inline, inline local function, intarray, intrinsics, int[], Invoke, Kotlin, kotlin hidden const, kotlin 의 숨겨진 비용, LAMBDA, Limit, local functions, Memory, multiple argument, nonnull type, null argument runtime check, null safety, nullable primitive type, nullable primitive types, optimization enable, optimize, outer function, Performance, performance-critical section, Primitive type, private function, proguard, proguard option, Reference Type, release build, specific invoke, spread operator, spread operator copy, unboxing, vararg, variable capture, Xno-param-assertions, [Kotlin] Kotlin 의 숨겨진 비용 #2, 숨겨진 비용, 자동 생성


Local functions


-

local function 의 limit 은 inline 으로 정의될 수 없다는 것 ( 글 쓴 당시까지는, 현재버전에서 꼭 체크해보라 ).

그리고 local function 을 가지고 있는 녀석도 inline 이 될 수 없다.

fun someMth(a: Int): Int {

    fun sumSquare(b: Int) = (a+b) * (a+b)

    return sumSquare(1) + sumSqaure(2)

}


local function 은 Function object 로 바뀐다.

public static final int someMath(final int a){

    Function1 sumSqure$ = new Function1(1){

        public Object invoke(Object var1){

            return Integer.valueOf(this.invoke((Number)var1).intValue()));

        }


        public final int invoke(int b){

            return (a+b) * (a+b);

        }

    }


    return sumSquare$.invoke(1) + sumSquare$.invoke(2);

}


그래도 변환된 것을 보면 lambda 에 비해 generic synthetic method 를 부르는 것이 아니라

직접 specific invoke 를 부르는 장점은 있다.


여기서 매번 new instance 가 만들어지는 것을 막으려면 capture 를 제거해주면 된다.

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



-

Local function 은 outer function 의 variable 에 접근 가능한 private function 의 다른 형태이다.

Local function 이 Function object 를 생성하는 단점은 있다. ( 어쩔 수 없다 )

그러나 잘 쓰려면 capturing 을 제거해서 매번 재생성하지 않고 한번 생성해서 reuse 하도록 하는 것이 좋다.





Null safety


-

Non-null arguments runtime checks


fun sayHello(who: String){

    println(“Hello $who”)

}


/* Generated code */

public static final void sayHello(@NotNull String who){

    Intrinsics.checkParameterIsNotNull(who, “who”);

    String var1 = “Hello “ + who;

    System.out.println(var1);

}



-

Intrinsics.checkParametersIsNotNull 은 param 이 null 일 경우 IllegalArgumentException 발생한다.

nonnull type 의 경우 이 코드가 자동으로 들어간다.


public function 일 경우 non-null argument 에 대해 항상 추가된다.

private function 의 경우는 불리지 않는다. compiler 가 null safe 를 보장해주기 때문이다.


nonnull check 하는 로직의 performance 이슈는 무시할만하다.



-

release build 에서 해당 option 을 빼고 싶다면 compiler option 을 주거나 proguard에 option 을 주면 된다.


compiler option

-Xno-param-assertions


proguard option ( optimization 이 enable 된 경우에만 )

-assumenosideeffects class kotlin.jvm.internal.Intrinsics{

    static void checkParameterIsNotNull(java.lang.Object, java.lang.String);

}



-

Nullable primitive types


nullable primitive type 의 경우는 항상 reference type 으로 작동한다.

이는 boxing, unboxing, memory 이슈 등을 야기할 수 있다.


nonnull 로 사용할 경우에는 가능하면 java의 int, float 형태 primitive 로 자동 치환된다.

따라서 가능하다면 primitive type 은 항상 nonnull 로 사용하자.



-

About arrays


primitive type 에 대해 다음의 3가지 형태의 array 정의가 가능하다. 예를 들면 int 에 대해

IntArray, Array<Int>, Array<Int?>


IntArray 의 경우는 compile 되면 int[] 로 변환되기 때문에 효율이 좋다.

Nullable primitive type 과 같은 이야기로 IntArray 를 쓰는 것이 좋다.





Varargs


-

Multiple argument 를 보낼 경우 Array object 를 만들어 넘겨준다.



-

Java 와 다른 점은 vararg 를 받는 곳에 바로 array 를 넘길 수도 있다는 것.

단, spread operator * 를 쓰긴 해야 한다.

이 때 주의해야 할 것은 이 경우 copied version 이 전달된다는 것.



-

argument 와 array (spreaded) 를 동시에 사용하는 케이스에는 더 특이하다.

fun printDouble(vararg values: Int){ … }


val values = arrayOf(1,2,3)

printDouble(0, *values, 42)


위의 코드는 아래와 같이 된다.

int[] values = new int[]{ 1. 2. 3 };

IntSpreadBuilder var10000 = new IntSpreadBuilder(3);

var10000.add(0);

var10000.addSpread(values);

var10000.add(42);

printDouble(var10000.toArray());


new array 를 생성하는 것과 동시에 임시 builder 까지 만든다.



-

위와 같은 원리를 이해하면 performance-critical section 에서 vararg 를 param 으로 가진 function 을 사용할 때 optimize 하기 쉽다.





반응형

댓글