Skip to content

Instantly share code, notes, and snippets.

@shahsurajk
Last active November 30, 2019 13:52
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shahsurajk/aa0ba330664e806924b58cd624053236 to your computer and use it in GitHub Desktop.
Save shahsurajk/aa0ba330664e806924b58cd624053236 to your computer and use it in GitHub Desktop.
Presentation on power of inline, given as on 23/03/19 at a BlrKotlin Meetup
        ##### ##                                                     
     ######  /###                                                    
    /#   /  /  ###                                                   
   /    /  /    ###        ##                                        
       /  /      ##        ##                                        
      ## ##      ##  /###   ##    ###    ####      /##  ###  /###    
      ## ##      ## / ###  / ##    ###     ###  / / ###  ###/ #### / 
    /### ##      / /   ###/  ##     ###     ###/ /   ###  ##   ###/  
   / ### ##     / ##    ##   ##      ##      ## ##    ### ##         
      ## ######/  ##    ##   ##      ##      ## ########  ##         
      ## ######   ##    ##   ##      ##      ## #######   ##         
      ## ##       ##    ##   ##      ##      ## ##        ##         
      ## ##       ##    ##   ##      /#      /  ####    / ##         
      ## ##        ######     ######/ ######/    ######/  ###        
 ##   ## ##         ####       #####   #####      #####    ###       
###   #  /                                                           
 ###    /                                                            
  #####/                                                             
    ###                                                                                                                          
              /##                                                    
            #/ ###                                                   
           ##   ###                                                  
           ##                                                        
           ##                                                        
   /###    ######                                                    
  / ###  / #####                                                     
 /   ###/  ##                                                        
##    ##   ##                                                        
##    ##   ##                                                        
##    ##   ##                                                        
##    ##   ##                                                        
##    ##   ##                                                        
 ######    ##                                                        
  ####      ##                                                                                                                      
                 ###                                                 
  #               ###    #                                           
 ###               ##   ###                                          
  #                ##    #                                           
                   ##                                                
###   ###  /###    ##  ###   ###  /###     /##                       
 ###   ###/ #### / ##   ###   ###/ #### / / ###                      
  ##    ##   ###/  ##    ##    ##   ###/ /   ###                     
  ##    ##    ##   ##    ##    ##    ## ##    ###                    
  ##    ##    ##   ##    ##    ##    ## ########                     
  ##    ##    ##   ##    ##    ##    ## #######                      
  ##    ##    ##   ##    ##    ##    ## ##                           
  ##    ##    ##   ##    ##    ##    ## ####    /                    
  ### / ###   ###  ### / ### / ###   ### ######/                     
   ##/   ###   ###  ##/   ##/   ###   ### #####                                                                                                            

BlrKotlin - 23/03/19

By Suraj Shah, Software Engg., QTalk

[e]: suraj@quiph.com
[pe]: shahsurajk@gmail.com
[g]: github.com/shahsurajk
[twtr]: @shahsurajk


  ______  ________          __  __       
 /      \|        \        |  \|  \      
|  $$$$$$\\$$$$$$$$______  | $$| $$   __ 
| $$  | $$  | $$  |      \ | $$| $$  /  \
| $$  | $$  | $$   \$$$$$$\| $$| $$_/  $$
| $$ _| $$  | $$  /      $$| $$| $$   $$ 
| $$/ \ $$  | $$ |  $$$$$$$| $$| $$$$$$\ 
 \$$ $$ $$  | $$  \$$    $$| $$| $$  \$$\
  \$$$$$$\   \$$   \$$$$$$$ \$$ \$$   \$$
      \$$$                               

			- A better dailer. 


[g]: github.com/quiph
[p]: https://play.google.com/store/apps/details?id=com.qtalk.app
[j]: https://angel.co/quiph/jobs


 ___       __   ___  ___  ________  _________  ________      
|\  \     |\  \|\  \|\  \|\   __  \|\___   ___\\_____  \     
\ \  \    \ \  \ \  \\\  \ \  \|\  \|___ \  \_\|____|\  \    
 \ \  \  __\ \  \ \   __  \ \   __  \   \ \  \      \ \__\   
  \ \  \|\__\_\  \ \  \ \  \ \  \ \  \   \ \  \      \|__|   
   \ \____________\ \__\ \__\ \__\ \__\   \ \__\         ___ 
    \|____________|\|__|\|__|\|__|\|__|    \|__|        |\__\
                                                        \|__|
                                                             

What are inline functions?

Inline functions in Kotlin stitch pieces of code together when used as a lambda.

Lambdas in Kotlin? What are they?

  • Also called as an higher-order function.
  • Introduced in Java 8.
  • Use functions as Objects.
  • Evaluated at runtime.

Disadvantages:

  • Increase runtime memory usage
  • Increase in object creation
  • Not backwards compatible

Example 1:

fun testLambdas(index: Int, myLambda: (index: Int) -> Unit){
    myLambda.invoke(index+1)
}

fun myBigLoop(){
    (0 .. 50).forEach {
        testLambdas(it) { lambdaValue-> 
            println(lambdaValue)
        }
    }
}

Decompile example 1:

public static final void myBigLoop() {
      byte var0 = 0;
      Iterable $receiver$iv = (Iterable)(new IntRange(var0, 50));
      Iterator var1 = $receiver$iv.iterator();

      while(var1.hasNext()) {
         int element$iv = ((IntIterator)var1).nextInt();
         int var4 = false;
         testLambdas(element$iv, (Function1)PresentationKt$myBigLoop$1$1.INSTANCE);
      }
   }

   // Kotlin complier generates a singleton for lambdas without closure. 
   // but for lambdas with closure, a new instance will be created everytime. 
   final class PresentationKt$myBigLoop$1$1 extends Lambda implements Function1 {
   public static final PresentationKt$myBigLoop$1$1 INSTANCE = new PresentationKt$myBigLoop$1$1();

   // Note boxing and unboxing of the int variable, this is painful too!
   // $FF: synthetic method
   // $FF: bridge method
   public Object invoke(Object var1) {
      this.invoke(((Number)var1).intValue()+1);
      return Unit.INSTANCE;
   }

   public final void invoke(int lambdaValue) {
      System.out.println(lambdaValue);
   }

   PresentationKt$myBigLoop$1$1() {
      super(1);
   }
}

In worst cases it can even create a new instance of the class Function1 for every iteration!



██╗  ██╗ ██████╗ ██╗    ██╗██████╗ 
██║  ██║██╔═══██╗██║    ██║╚════██╗
███████║██║   ██║██║ █╗ ██║  ▄███╔╝
██╔══██║██║   ██║██║███╗██║  ▀▀══╝ 
██║  ██║╚██████╔╝╚███╔███╔╝  ██╗   
╚═╝  ╚═╝ ╚═════╝  ╚══╝╚══╝   ╚═╝   
                                   

How does the inline keyword help?

When you use the inline keyword with a function, the compiler takes in extra effort and places the codeblock inside the parent function scope instead of using objects.

TLDR; It pastes the code inside the lambda block to the parent function.

Reiterating example 1:

What we really wanted with example 1,

fun myBigLoop(){
	(0..50).forEach{
		val finalVal = it+1
		println(finalVal)
	}
}

This would be the best case scenario right?

Adding inline to example 1:

Example 2:

inline fun testLambdas(index: Int, myLambda: (index: Int) -> Unit){
    myLambda.invoke(index+1)
}

Result?

 __   __            _       _               _    
 \ \ / /   ___     (_)     | |    __ _     | |   
  \ V /   / _ \    | |     | |   / _` |    |_|   
  _\_/_   \___/   _|_|_   _|_|_  \__,_|   _(_)_  
_| """"|_|"""""|_|"""""|_|"""""|_|"""""|_| """ | 
"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-' 

public final class PresentationKt {
	// the compiler generated a pure lambda function as well, just for compatibility sake. 
   public static final void testLambdas(int index, @NotNull Function1 myLambda) {
      int $i$f$testLambdas = 0;
      Intrinsics.checkParameterIsNotNull(myLambda, "myLambda");
      myLambda.invoke(index + 1);
   }

   public static final void myBigLoop() {
      byte var0 = 0;
      Iterable $receiver$iv = (Iterable)(new IntRange(var0, 50));
      Iterator var1 = $receiver$iv.iterator();

      while(var1.hasNext()) {
         int element$iv = ((IntIterator)var1).nextInt();
         int var4 = false;
         int $i$f$testLambdas = false;
	 // the logic inside the lambda, performed here locally! Magical isn't it?
         int lambdaValue = element$iv + 1;
         int var7 = false;
         System.out.println(lambdaValue);
      }
   }

The only difference between example 1 and example 2 is the inline keyword in front of the function!



 +-+-+-+-+-+-+-+          
 |i|n|l|i|n|e|,|          
 +-+-+-+-+-+-+-+          
 +-+-+-+-+-+-+-+-+-+-+-+-+
 |c|r|o|s|s|i|n|l|i|n|e|,|
 +-+-+-+-+-+-+-+-+-+-+-+-+
 +-+-+-+-+-+-+-+-+        
 |n|o|i|n|l|i|n|e|        
 +-+-+-+-+-+-+-+-+        

inline

inline takes the block inside the lambda and stitches it into the parent function.

If a function doesn't actually need inlining, the compiler throws a warning saying "Performance impact" cause there's nothing to be inlined, yet the lambda is marked as inline which forces the compiler to perform more effort!

Consider this extention to the original example 2,

Example 3:

    inline fun wastedInline(index: Int){
        testLambda(index)
    }

Ths inline modifier here is of no use cause there's nothing to be inlined!

crossinline

This is an argument specifier, if an argument in an inlined function is marked as crossinline it will not allow the function to have a non-local return.

What's a non-local return?

Simple, anything which is not local!

Example 4:

fun myEmptyFunction(){
    return crossInlineTry { 
        // this will throw an error saying return not allowed here
        return
        
        // this wont, see how we're just returning from the current scope. The parent will still perform operations
        // after this line
        return@crossInlineTry
    }
}

inline fun crossInlineTry(crossinline csi: ()->Unit){
    csi.invoke()
}

Why do we need crossinline then?

  • Perform operations after the lambda
  • Restrict non-local returns.

Best example, forEach! Kotlin doesn't have break, break can be implemented by making the value capture inside forEach as crossinline (note the _Collections.kt class doesn't have this, this is just a thought.) thus instead of break one can simply do return@forEach which currently does work of a continue!

noinline

A no brainer, but as the doc says:

In case you want only some of the lambdas passed to an inline function to be inlined

Example 5:

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { ... }

Why do we need noinline then?

  • For reusability, if you're have an existing java based lambda, to use it in compatiblity with kotlin.
  • There could also be a scenario where inline-ing something can cause compilation errors etc.

  _____ _           ___              _            
 |_   _| |_  ___   / __|___  ___  __| |           
   | | | ' \/ -_) | (_ / _ \/ _ \/ _` |_          
   |_| |_||_\___|  \___\___/\___/\__,_( )         
  _   _          _             _      |/        _ 
 | |_| |_  ___  | |__  __ _ __| |  __ _ _ _  __| |
 |  _| ' \/ -_) | '_ \/ _` / _` | / _` | ' \/ _` |
  \__|_||_\___| |_.__/\__,_\__,_| \__,_|_||_\__,_|
  _   _                    _      _               
 | |_| |_  ___   _  _ __ _| |_  _| |              
 |  _| ' \/ -_) | || / _` | | || |_|              
  \__|_||_\___|  \_,_\__, |_|\_, (_)              
                     |___/   |__/                 

The Good:

  1. Saves memory
  2. Reduces runtime overload
  3. Helps making code modular, while still making use of the underlying best practices
  4. Many, many components in Kotlin as based on inline functions,
    • Coroutines
    • reified functions
    • Standards.kt helper functions (forEach, also etc)

The Bad:

  1. inline functions are evaluated at compile-time, thus increasing overall compile time taken
  2. Value captured inside a closure can be misleading sometimes.
  3. "Developer error" of forgetting the inline keyword! (there are lint errors for using inline improperly suggesting performance impact, but not the other way around!)

The Ugly:

  1. Sometimes it becomes difficult to keep track and to remember proper usages of inline, crossinline and noinline


'########:'##::::'##:'########:'##::::'##:'########::'########:
 ##.....:: ##:::: ##:... ##..:: ##:::: ##: ##.... ##: ##.....::
 ##::::::: ##:::: ##:::: ##:::: ##:::: ##: ##:::: ##: ##:::::::
 ######::: ##:::: ##:::: ##:::: ##:::: ##: ########:: ######:::
 ##...:::: ##:::: ##:::: ##:::: ##:::: ##: ##.. ##::: ##...::::
 ##::::::: ##:::: ##:::: ##:::: ##:::: ##: ##::. ##:: ##:::::::
 ##:::::::. #######::::: ##::::. #######:: ##:::. ##: ########:
..:::::::::.......::::::..::::::.......:::..:::::..::........::

  • inline classes. Experimental as of 1.3.21 but gets all the advantages of an inline function to the class level!

    • Inline classes are not present during compile time, thus saving a lot of memory!
    • Plus they make good database IDs - Jake Wharton (inline classes refactored themselves after he said this :P)
    • For now inline classes support only one variable, but who knows!
  • A very active community.

  • KEEP: Kotlin Evolution and Enhancement Process, JetBrain's way of asking the community for enhancements and showcasing to the world as well what it can do! Some notable things implemented due to KEEP

    • Coroutines, well, let's just not talk about this.
    • Inline classes, still a WIP but I use it in production!
    • Unsigned int, long. The minute you realize that this is just a smarter way, a textbook implementation of inline classes, you'll just be mesmerizied by the boundaries kotlin has set for devs!

  _____ _                 _                        
 |_   _| |__   __ _ _ __ | | __  _   _  ___  _   _ 
   | | | '_ \ / _` | '_ \| |/ / | | | |/ _ \| | | |
   | | | | | | (_| | | | |   <  | |_| | (_) | |_| |
   |_| |_| |_|\__,_|_| |_|_|\_\  \__, |\___/ \__,_|
                                 |___/             

Appendex:

  1. Inline classes make great database Ids: link
  2. Kotlin KEEP: link
  3. Inline functions: link

Heartly thanks to: ASCII Art generator link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment