Kotlin
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Null Safety
-
Section 4: Object-Oriented Kotlin
-
Section 5: Functional Kotlin
-
Section 6: Coroutines
-
Section 7: Collections Deep Dive
-
Section 8: Type System Deep Dive
-
Section 9: Interop and Tooling
-
Section 10: Kotlin DSLs and Patterns
-
Section 11: Testing and Quality
-
Section 12: Server-Side Kotlin
-
Section 13: Practical Projects
-
Section 14: Interview Practice
-
Section 15: More Practice Exercises
-
Section 16: More Standard Library
-
Section 17: Multiplatform Kotlin
-
Section 18: kotlin.collections In Depth
-
Section 19: kotlin.text In Depth
-
Section 20: kotlin.ranges and kotlin.sequences
-
Section 21: kotlin.io and File Handling
-
Section 22: kotlinx.coroutines Deep Dive
-
Section 23: kotlin.reflect
-
Section 24: Android Development with Kotlin Overview
-
Section 25: Kotlin Multiplatform Deep Dive
-
Section 26: Kotlin for Backend Deep Dive
-
Section 27: Kotlin Design Patterns
-
Section 28: Advanced Language Features
-
Section 29: More Practice Exercises
-
Section 30: More Interview Practice
-
Section 31: Kotlin Type System Deep Dive
-
Section 32: Kotlin Null Safety Advanced
-
Section 33: Kotlin Testing Deep Dive
-
Section 34: Kotlin Build Tooling Deep Dive
-
Section 35: Kotlin Serialization
-
Section 36: Kotlin Performance Considerations
-
Section 37: Kotlin Native Overview
-
Section 38: Kotlin for Data and Scripting
-
Section 39: More Coroutines Practice
-
Section 40: More Android-Adjacent Patterns
-
Section 41: More Practical Projects
-
Section 42: More Design and Architecture Practice
-
Section 43: Kotlin Language Evolution
-
Section 44: More Interview and Review
-
Section 45: Kotlin Delegation Patterns Deep Dive
-
Section 46: Kotlin Annotations Deep Dive
-
Section 47: Kotlin for Gradle Plugin Development
-
Section 48: Kotlin Concurrency Beyond Coroutines
-
Section 49: Kotlin Compiler Internals
-
Section 50: Real-World Kotlin Case Studies
-
Section 51: Final Practice Projects
-
Section 52: Kotlin for Server-Side Reactive Programming
-
Section 53: More Practice and Drills
-
Section 54: Kotlin Security Practices
167: Inline Functions and Performance
Imagine you're following a complex recipe to bake a cake. Halfway through, the instructions say, "Now, refer to page 42 to learn how to make the frosting." You have to stop what you're doing, flip through the book, find page 42, follow those steps, and then flip back to where you left off to finish the cake. In a simple recipe, that's no big deal. But if the recipe told you to "refer to page 42" every single time you added a sprinkle of sugar, you'd spend more time flipping pages than actually baking.
In Kotlin, when you pass a lambda to a function, the JVM treats that lambda as an object. It's essentially "flipping to page 42." The system has to create a function object, manage the call stack, and jump to a different piece of memory. If you're doing this inside a loop that runs 10,000 times, those "page flips" start to add up and slow your app down.
The hidden cost of lambdas
You've been using higher-order functions for a while now, and for most cases, the performance hit is negligible. However, I want you to see what's actually happening under the hood. Consider a simple utility function that executes a block of code and logs the result:
fun performTask(action: () -> Unit) {
println("Starting task...")
action()
println("Task finished.")
}
Every time you call performTask { println("Doing work") }, Kotlin creates an instance of a Function object to hold that lambda. If this is called in a high-frequency loop—say, inside a custom animation frame or a heavy data processing pipeline—you're putting unnecessary pressure on the Garbage Collector by creating thousands of short-lived objects.
Telling the compiler to copy-paste
This is where the inline keyword comes in. When you mark a function as inline, you're telling the compiler: "Don't actually call this function. Instead, just copy the code of the function and the code of the lambda directly into the place where I called it."
Let's rewrite that utility:
inline fun performTask(action: () -> Unit) {
println("Starting task...")
action()
println("Task finished.")
}
Now, if you call performTask { println("Doing work") }, the compiled bytecode doesn't contain a function call at all. It literally becomes:
println("Starting task...")
println("Doing work")
println("Task finished.")
The "page flip" is gone. The code is injected directly into the flow, and no lambda object is ever created. It's a pure performance win for higher-order functions.
When inlining becomes too much
You might be wondering, "Why not just make every single function inline?" I've seen developers do this, and it's a mistake. If you inline a massive function that is called in fifty different places, your final binary size will bloat because the compiler is copy-pasting that huge block of code fifty times. Use inline specifically for functions that take lambdas as arguments.
Sometimes, you might have a function with multiple lambdas, but you only want one of them to be inlined. In those cases, you can use the noinline modifier. Similarly, if your inline function passes a lambda to another block (like a nested try-catch) where a "non-local return" would be dangerous, you'll need crossinline. But for now, just remember the golden rule: inline for performance when lambdas are involved, but don't overdo it.
📋 Practical Task
Building a High-Precision Benchmarking Utility
You need to create a utility function called benchmark that measures how long a specific block of code takes to execute. To ensure the measurement is accurate and doesn't include the overhead of object creation, this function must be implemented as an inline function.
Requirements:
- Create an
inlinefunction namedbenchmarkthat takes a lambda as its only parameter. - Inside the function, capture the start time using
System.nanoTime(). - Execute the lambda.
- Capture the end time and calculate the difference.
- Print the result in the format:
"Execution time: [X] ns". - In your
mainfunction, usebenchmarkto measure a loop that sums numbers from 1 to 1,000,000.
There are no comments for now.