Использовать встроенное ключевое слово для небольших частных функций в kotlin? - PullRequest
2 голосов
/ 26 мая 2019

Я хотел бы знать, если это хорошая идея встроить небольшие, частные функции? Эти функции, в моем случае, предназначены только для удобства чтения, и я знаю, что они вызываются только несколько раз, поэтому больший размер байт-кода незначителен.
Я знаю, что прирост производительности также может быть незначительным, потому что я не передаю функциональные типы (и компилятор фактически предупреждает меня об этом), но давайте предположим, что это горячая точка в нашем приложении.

Фактический пример:

Я теоретически бесконечная лента символов (как у машины Тьюринга), которая моделируется буксирными массивами (слева и справа для положений <0 и положений> = 0 соответственно). Теперь у меня есть операции чтения и записи, которые, как предполагается, называются много.

В Java у меня было:

/**
 * @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;
}

Теперь я хотел перевести фрагмент в kotlin, прочитать о встроенных функциях и придумать следующее:

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 }

Лично я считаю, особенно функции чтения, легко читаемыми.
Так что это хорошая идея и могу ли я проигнорировать предупреждение IDE? Или я что-то упустил? Может быть, есть лучшая практика или другой шаблон для написания этих функций без повторения (почти) одних и тех же строк дважды (для позиции <0 и позиции> = 0).

1 Ответ

5 голосов
/ 26 мая 2019

Нет необходимости вставлять очень маленькую многократно используемую функцию, поскольку JVM JIT, скорее всего, сделает это за вас, если сочтет это целесообразным.Если вы встраиваете функцию в функцию, вы вызываете небольшое раздувание в сгенерированном байт-коде, но в противном случае там нет большого вреда для функций, которые не вызываются из многих разных точек в вашем коде.Раздувание байт-кода хуже для больших встроенных функций, функций, используемых во многих местах вашего кода, и встроенных функций, которые вызывают другие встроенные функции.Ваш случай не вызывает много вообще.

Короткие методы в значительной степени выигрывают от встраивания и, как вы можете прочитать в Когда окупаются короткие методы: Встраивание JIT :

Метод подходит для встраивания, если:

  • Он небольшой - размер байт-кода меньше 35 байт (может быть переопределен с помощью флага -XX: MaxInlineSize = X).
  • Он часто вызывается (он горячий) и его размер меньше 325 байт (может быть переопределен флагом -XX: MaxFreqInlineSize = X).

Если вы превысите этипараметры или вы хотите предварительно подогреть встраивание, вы можете использовать ключевое слово inline, чтобы оно происходило во время компиляции.

Вы также хотели бы использовать inline для небольших функций, когда:

  • Вы хотите, чтобы лямбда, передаваемый в функцию, выполнял нелокальный возврат
  • Вы хотите использовать уточненные параметры универсального типа .
  • Вы пишете функцию, которая принимает лямбду, которую она использует в hПривет, повторение, и вы хотите избежать издержек вызова функции для лямбды.Благодаря встроенному в этот тип функции компилятору Kotlin также добавляется лямбда-выражение.
...