Inlining lambdas

Calling a function always incurs a slight (negligible in most cases) overhead, no matter what kind of programming language you use. And in the case of Kotlin and function types, there is also the overhead of allocating memory on the heap, since function types are objects.

To avoid this overhead, Kotlin offers the inline keyword, which can be placed before the fun keyword when declaring a function. Inlining can be described as moving the functionality of a called function to its caller function.

To see the difference between inline and normal functions, let's declare functions with same body and signature, but one will be inlined:

fun noInline(func: () -> String) {
val str = func()
println("Func produced: $str")
}

inline fun inlined(func: () -> String) {
val str = func()
println("Func produced: $str")
}

And now, let's see what the compiler does with both of these functions. This is Java bytecode decompiled back to Java:

public static final void noInline(@NotNull Function0 func) {
Intrinsics.checkParameterIsNotNull(func, "func");
String str = (String)func.invoke();
String var2 = "Func produced: " + str;
System.out.println(var2);
}

public static final void inlined(@NotNull Function0 func) {
Intrinsics.checkParameterIsNotNull(func, "func");
String str = (String)func.invoke();
String var3 = "Func produced: " + str;
System.out.println(var3);
}

They get compiled exactly the same. But that's not a surprise; we are not calling the inline function, so there is nothing to inline. Let's now see the difference when we call each of them:

fun callInlined() {
inlined { "Kotlin" }
}

fun callNotInlined() {
noInline { "Kotlin" }
}

First, let's see the not-inlined function:

public static final void callNotInlined() {
noInline((Function0)$callNotInlined$1.INSTANCE);
}

There is the function call and as an argument, an instance of the function type is provided. This instance comes from the extra class that the compiler generated, and it looks something like this:

public class $callNotInlined$1 extends kotlin.jvm.internal.Lambda implements Function0<String> {

private $callNotInlined$1() {
super(0);
}

@Override
public String invoke() {
String str$iv = "Kotlin";
return str$iv;
}

public static final $callNotInlined$1 INSTANCE;

static {
INSTANCE = new $callNotInlined$1();
}
}

So, for the normal function call, without inlining, the compiler allocates an extra object on the heap and stores it to a static field, so the same instance of the function type is reused whenever this function type is needed.

Now, let's take a look at the bytecode output of the inlined function:

public static final void callInlined() {
String str$iv = "Kotlin";
String var1 = "Func produced: " + str$iv;
System.out.println(var1);
}

The inlined function doesn't have the extra class or the extra function call. The compiler took the code from the lambda that represents our function type and placed that code directly into the call site, thus avoiding a function call and the creation of an additional object on the heap.

The inline keyword is also allowed on property accessor methods:

var str: String
inline get() = "Kotlin"
inline set(value) {
println("$value passed as parameter")
}

Also, only one property method can be inlined:

var str: String
get() = "Kotlin"
inline set(value) {
println("$value passed as parameter")
}

Or, the property itself can have the inline keyword, which then implies both get and set methods are inlined:

inline var str: String
get() = "Kotlin"
set(value) {
println("$value passed as parameter")
}

You can place the inline keyword even if your function doesn't accept a function type as a parameter:

inline fun nothingToInline() {

}

In this case, the compiler will generate a warning only, saying that the performance impact is negligible and that you should use inline only with function type parameters. This is a good warning and can be used as a guide for when to use the inline keyword. If you just want to avoid an additional function call, don't use inline; leave that optimization to the runtime. If your function accepts a function type parameter, then inlining can avoid creating an instance of both a class and a function call.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.136.168