[Kotlin] Kotlin 의 숨겨진 비용 #2
https://medium.com/@BladeCoder/exploring-kotlins-hidden-costs-part-2-324a4a50b70
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 하기 쉽다.
다음 글 : [Kotlin] Kotlin 의 숨겨진 비용 #3
'프로그래밍 놀이터 > Kotlin, Coroutine' 카테고리의 다른 글
[Kotlin] initializer 이야기 (0) | 2018.01.19 |
---|---|
[Kotlin] Kotlin 의 숨겨진 비용 #3 (0) | 2018.01.18 |
[Kotlin] Kotlin 의 숨겨진 비용 #1 (2) | 2018.01.16 |
[Kotlin] 장점, 단점, 그리고 아쉬운 점 이야기 (0) | 2018.01.15 |
[Kotlin] Kotlin 은 Compile time 이 느리다는데.. 사실일까? (0) | 2017.09.26 |
댓글