What is inlining?
Inlining is basically requesting the compiler to copy the (inlined) code at the calling place.
Why it is required?
When a program is executed and a function is called, CPU stores the memory address of the instruction following the function call, copies the arguments of the function into a stack and finally transfers control to the specified function. The CPU then executes the function code, stores the function return value in a predefined memory location/register and returns control to the calling function.
In Kotlin, the higher order functions or lambda expressions, all stored as an object which takes some memory and might increase the memory overhead. Sometimes, the function does a very basic functionality that passing the control is not even worth but as it (the function) is being used at multiple places, we need to create a separate function. To reduce the memory overhead of such functions we can use the inline keyword which ultimately requests the CPU to not allocate any memory for the function and simply copy the body of that function at the calling place.
Let’s take an example.
suppose we have a function which simply checks if the first argument is a multiple of the second argument and returns a boolean.
fun isMultipleOf (number: Int, multipleOf : Int): Boolean{
return number % multipleOf == 0
}
Now to use this function as a higher order function,
fun <T> ArrayList<T>.filterOnCondition(condition: (T) -> Boolean): ArrayList<T>{
val result = arrayListOf<T>()
for (item in this){
if (condition(item)){
result.add(item)
}
}
return result
}
var list = arrayListOf<Int>()
for (number in 1..10){
list.add(number)
}
val resultList = list.filterOnCondition { isMultipleOf(it, 5) }
print(resultList)
In the above example, the functionality of the isMultipleOf is so small that it can be copied but as it can be used at multiple places, we created it as a separate function. As this function is passed as an argument to filterOnCondition function, CPU will assign it to some object which will take some memory. Also, for every item in the list, the CPU will jump to the memory address of isMultipleOf function and will pass back to result.
To prevent this, we can make the isMultipleOf function as inline which will as the compiler to copy it to the calling place which will prevent the memory allocation of the function and jumping of the CPU at runtime.
inline fun isMultipleOf (number: Int, multipleOf : Int): Boolean {
return number % multipleOf == 0
}
The functionality will remain the same.
Things to keep in mind
- We will lose access to any private variable or function of the class inside the inline function. So it’s better to make functions inline which are very generic and don’t use a class level variable or function to achieve their functionality.
private var localVariable : Int = 8
inline fun isMultipleOf (number: Int, multipleOf : Int): Boolean {
print(localVariable) //compilation error
return number % multipleOf == 0
}
The above code will show compilation error
<e>Public-API inline function cannot access non-public-API 'private var localVariable</e>
Which is clear in itself.
- Do not inline for bigger functions as it degrades the performance.