Use inline keyword for small, private functions in kotlin?
Asked Answered
L

1

9

I would like to know, if it is a good idea to inline small, private functions? These functions, in my case, are only there for readability purposes and I know, that they are only called a few times, so the bigger bytecode size is insignificant.
I know, that the performance boost may also be insignificant, because I am not passing functional types (and the compiler actually warns me about that), but lets assume that it is a hot spot in our application.

Actual example:

I hava a theoretically infinite tape of symbols (like the one of a turing machine) which is modeled by tow arrays (left and right for positions < 0 and positions >= 0, respectively). Now I have read and write operations which are assumed to be called a lot.

In java I had:

/**
 * @param cell the cell
 * @return the symbol at the specified cell
 */
public char read(int cell) {
    char[] tape;
    if (cell < 0) {
        tape = left;
        cell = -cell - 1;
    } else {
        tape = right;
    }
    return cell < tape.length ? tape[cell] : blank;
}

/**
 * Writes a symbol to the specified cell.
 * @param c    the symbol
 * @param cell the cell
 */
public void write(char c, int cell) {
    char[] tape;
    if (cell < 0) {
        cell = -cell - 1;
        if (cell >= left.length) left = expandArray(left, cell, blank);
        tape = left;
    } else {
        if (cell >= right.length) right = expandArray(right, cell, blank);
        tape = right;
    }
    tape[cell] = c;
}

Now I wanted to translate the snippet to kotlin, read about inline functions and come up with this:

fun read(cell: Int = headPosition) = when {
    cell < 0 -> read(left, -cell - 1)
    else     -> read(right, cell)
}

private inline fun read(tape: CharArray, cell: Int): Char {
    return if (cell < tape.size) tape[cell] else blank
}

fun write(c: Char, cell: Int = headPosition) = when {
    cell < 0 -> left = write(c, left, -cell - 1)
    else -> right = write(c, right, cell)
}

private inline fun write(c: Char, tape: CharArray, cell: Int): CharArray = when {
    cell >= tape.size -> expandArray(tape, cell, blank)
    else -> tape
}.also { it[cell] = c }

I personally think, especially the read functions, are easy to read.
So is this a good idea and can I ignore the warning of the IDE? Or am I missing something? Maybe there is a best practice or other pattern to write these functions without repeading (almost) the same lines twice (for position < 0 and position >= 0).

Littrell answered 26/5, 2019 at 11:27 Comment(1)
You would have to measure (which is hard to do correctly in Java). But I would follow the IDE's advice. The JIT will probably inline the code anyway if it's a hot spot.Farmstead
P
12

There is no need to inline a very small repeatedly used function since the JVM JIT will likely do this for you anyway when it deems it to be appropriate. If you inline the function you do cause a bit of bloat in the generated bytecode but otherwise, there isn't a lot of harm there either for functions that are not called from many different points in your code. Bytecode bloat is worse for larger inline functions, functions used from many places in your code, and inline functions that call other inline functions. Your case does not cause much at all.

Short methods benefit heavily from inlining and as you can read in When Short Methods Pay Off: JIT Inlining:

A method is eligible for inlining if:

  • It’s small – the bytecode size is smaller than 35 bytes (can be overridden by the -XX:MaxInlineSize=X flag).
  • It’s called frequently (it’s hot) and it’s smaller than 325 bytes (can be overridden by the -XX:MaxFreqInlineSize=X flag).

If you exceed these parameters or want to pre-warm up the inlining you can use the inline keyword to cause it to happen at compilation time.

You also would want to use inline on small functions when:

  • You want to allow a lambda passed to the function to do a non-local return
  • You want to use reified generic type parameters.
  • You are writing a function that accepts a lambda that it uses in high repetition and you want to avoid the function call overhead to the lambda. By inlining this type of function, the Kotlin compiler also inlines the lambda.
Pantomimist answered 26/5, 2019 at 11:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.