Funciones inline en Kotlin

video[Tutorial Funciones inline en Kotlin

En una función normal su código es almacenado una vez en memoria. Cada vez que es invocada, se añaden los parámetros en la pila y se invoca.
En una función inline no se realiza esta invocación, sino que el código de la función es insertado cada vez que se utiliza.
En teoría, una función inline tiene mayor rendimiento frente a las normales, debido a que evitan usar la pila para pasar parámetros y evitan las instrucciones de salto y retorno. Esta afirmación va a depender de cómo se compile el código. Por ejemplo, en la máquina virtual Java, usa la pila incluso para ejecutar instrucciones en código máquina. Debido a que el código se tiene que «copiar» cada vez que se llame a dicha función, el tamaño del ejecutable aumentará conforme el tamaño de la función de tipo inline crezca.
Puede resultar conveniente utilizar una función inline si se encuentra en un bucle y va a ser llamada muchas veces. Pero en la mayoría de los casos es mejor no utilizarlas. Para convertir una función inline simplemente anteponemos esta palabra en su declaración:

inline fun suma(a:Int, b:Int) = a + b
val c = suma(2, 2) 
La verdadera ventaja de las funciones inline aparece cuando se aplican a funciones de orden superior. Las funciones de orden superior presentan cierta penalización de rendimiento (tanto en tiempo como en memoria). Para llamar al parámetro Lambda se crea un nuevo objeto en memoria que contiene un método con los parámetros indicados en el Lambda. Por ejemplo, dada la función:
fun opera(l: List, v: Int, funcion: (Int, Int)->Int): List 
al compilar la siguiente llamada:
opera(l, v) { a, b -> a + b } 
El código resultante sería algo parecido como si en Java hubiéramos escrito:
opera(l, v, new Funcion() { 
   @Override public int funcion(int a, b) { return a + b; }
}); 
Lo que supone la creación de un nuevo objeto anónimo[1]. Pero si, por el contrario, hubiéramos escrito:
inline fun opera(l: List<Int>, v: Int, funcion: (Int, Int)->Int): List<Int> 
El inline se aplicará no solo a opera sino también a funcion. El resultado final será insertar directamente el código de opera, y luego dentro de este código reemplazar funcion por su código. Quedaría así:
val result = arrayListOf<Int>()
for (item in lista)
   result.add(funcion(item, valor) item + valor)
return result 
En caso de una lista de 100.000 elementos, hemos evitado la creación de 100.000 objetos. Lo que supone una importante mejora. Unido al hecho de que nos hemos evitado 10.000 llamadas a la función.

Ejercicio: Comparativa Lambdas con y sin funciones inline.

En este ejercicio vamos a tratar de demostrar la influencia de usar Lambdas en funciones normales y en funciones inline. Analizaremos tanto el rendimiento temporal como el uso de memoria.
1. Crea un nuevo proyecto en Android Studio con los siguientes datos:
   Application Name: Funciones Inline
   Package name: com.example.funcionesinline
   - Including Kotlin support
   - Phone and Tablet
      Minimum SDK: API 16: Android 4.1 (Jelly Bean)
   Basic Activity

Deja el resto de valores por defecto.   
2. Añade la siguiente función en MainActivity.
inline fun opera(l: IntArray, v: Int, funcion:(Int, Int)-&gt;Int): IntArray {
    val result = IntArray(l.size)
    for ((indice, item) in l.withIndex())
        result[indice] = (funcion(item, v))
    return result
}
3. En el método onCreate añade dentro de fab.setOnClickListener:
fab.setOnClickListener { view ->
    var lista = IntArray( 5, { 2 } )
    val ini = System.currentTimeMillis()
    for (n in 1..100_000) {
        opera(lista, 2) { a, b -> a+b }
    }
    val fin = System.currentTimeMillis()
    Snackbar.make(view, "Tiempo ${fin-ini}", Snackbar.LENGTH_LONG)
            .setAction("Action", null).show()
} 
 Comenzamos inicializando un array de enteros de 5 elementos y de valor 2. Observa como el valor con el que inicializamos los elementos se indica mediante un Lambda. Anotamos el tiempo de inicio. Realizamos 100.000 llamadas a opera y anotamos el tiempo final. Finalmente mostramos el tiempo transcurrido en un Snackbar.
4. Ejecuta la aplicación y apunta el tiempo transcurrido.
5. Quita el modificador inline de opera.
6. Ejecuta de nuevo la aplicación y apunta el tiempo transcurrido. Este ha de ser significativamente mayor.
7. Para realizar un estudio de la memoria utilizada pulsa en el botón:

8. Haz clic en la parte central MEMORY.
9. Pulsa el botón Record Memory Alocation  .
10. Pulsa el botón flotante de la aplicación y cuando muestre el tiempo transcurrido pulsa en Stop Recording.
11. Captura la pantalla para poder analizarla con posterioridad.
12. Añade el modificador inline de opera.
13. Repite el proceso y compara los resultados ¿Ha habido una diferencia significativa de uso de memoria?El tema de las funciones es más extenso. Por ejemplo, en próximos puntos veremos funciones de extensión.
 
Preguntas de repaso: Lambdas