androidkotlinandroid-reflection

android kotlin - are Function References slow due to reflection?


Reflection is suppose to be a bit time consuming on android. so i was wondering given a function reference like this:

fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd))

is ::isOdd call an unnecessary burden ?

would it be more efficient to not use them ?

UPDATE: doing some light metrics i did the following:

    class MainActivity : Activity() {

    val numbers = listOf(1, 2, 3)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        doRegular()
        doReflection()
    }

    fun isOdd(x: Int) = x % 2 != 0

    fun doRegular() {
        val start = System.currentTimeMillis()
        for (i in 0..999999) {
            numbers.filter { it % 2 != 0 }
        }
        Log.v("myTag", "regular: +${System.currentTimeMillis() - start}")
    }

    fun doReflection() {
        val start = System.currentTimeMillis()
        for (i in 0..999999) {
            numbers.filter(::isOdd)
        }
        Log.v("myTag", "reflection: ${System.currentTimeMillis() - start}")
    }
}

and the print statement results are:

//*****this was the first attempt before compiler could optimise anything
        regular: +577     
        reflection: 318
  //*********
  
    regular: +376
    reflection: 327
    
    regular: +593
     reflection: 343
    
     regular: +417
     reflection: 251
     
     regular: +243
     reflection: 238
     
     regular: +241
     reflection: 238
     
     regular: +238
     reflection: 237
     
     regular: +235
     reflection: 247
     
     regular: +237
     reflection: 247
     
     regular: +230
     reflection: 268

What would you conclude given these results?

update: some are asking why i think its using reflection. its based on this:

This stackoverflow answer seems to state its reflection: and the title for the official doc has reflection as the main heading: hence my confusion.


Solution

  • Surely ::isOdd is used for referencing the function, but unless it is really required to use the "reflection", it (reflection) is not used. Similar to how Int are changed into int in the bytecode unless we need to store the reference, if we create collections then only it is represented as java.lang.Integer.

    The Kotlin compiler is smart and does the trick behind the hood.

    /**
     * Returns a list containing only elements matching the given [predicate].
     * 
     * @sample samples.collections.Collections.Filtering.filter
     */
    public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
        return filterTo(ArrayList<T>(), predicate)
    }
    

    The filter function is inline function that embeds the lambda/reference directly at the call-site without actually using the reflection.

    If you see the JVM bytecode, you'll see the following decompiled Java code:

    // of numbers.filter { it % 2 != 0 }
    while(var6.hasNext()) {
        Object element$iv$iv = var6.next();
        int it = ((Number)element$iv$iv).intValue();
        int var9 = false;
        if (it % 2 != 0) {              // <- check this out
            destination$iv$iv.add(element$iv$iv);
        }
    }
    
    // of numbers.filter(::isOdd)
    while(var6.hasNext()) {
        Object element$iv$iv = var6.next();
        int p1 = ((Number)element$iv$iv).intValue();
        int var9 = false;
        if (isOdd(p1)) {                  // <- check this out
            destination$iv$iv.add(element$iv$iv);
        }
    }
    

    Realistically there is no reflection is involved here.

    Sidenote: I've had a similar question over the official Kotlinlang slack at here, and I've got much attention but only 1 reply regarding it is only about metafacory as stated here in softwareengineering stackexchange subdomain.

    Here's a ss: enter image description here