※本サイトで紹介している商品・サービス等の外部リンクには、アフィリエイト広告が含まれる場合があります。

9-3. インライン関数

2023年9月9日

概要

プログラミングにおけるインラインやインライン展開とは、関数などで定義した処理を関数を呼び出すのではなく、呼び出した箇所に直接埋め込んでしまうことを示します。
これにより、関数呼び出しのためのオーバヘッドを無くしパフォーマンスの改善ができる場合があります。
ただし、埋め込んでしまうのでコンパイル後のバイトコードが、関数をうまく利用した場合よりも長くなる場合があります。

Kotlin では、バイトコードにコンパイルする際にインライン化されるように、関数を定義することが可能です。(インライン関数として定義)

定義方法

関数の定義の fun 前に inline キーワードを付けることで、インライン関数を定義できます。

例えば、以下などです。

inline fun 関数名(パラメータの変数名: パラメータの型) : 関数が返す変数の型 {
    関数本体
    return 関数が返す値
}

インライン関数は、コンパイル時に呼び出し元のコードに展開されます。 つまり、インライン関数を呼び出すと、コンパイラは関数の内容を呼び出し元にコピーし、あたかも関数がそこに直接書かれているかのように処理します。 これにより、 関数呼び出しのオーバーヘッドが解消され、パフォーマンスが向上します。
インライン関数は、特に高階関数を使用する場合に効果的です。 高階関数は、関数を引数として受け取ったり、戻り値として返す関数ですが、実行時に匿名クラスのインスタンスを生成するため、メモリ割り当てや仮想関数呼び出しなどのオーバーヘッドが発生します。 インライン関数は、このオーバーヘッドを解消することで、高階関数の使用に伴うパフォーマンスの低下を防ぎます。

関数とインライン関数の比較

インライン関数が実際にインライン化されているかどうかを確認するには、逆コンパイルツールを使用して、生成されたバイトコードを確認します。
インライン化されている場合、 関数の呼び出しではなく、関数の内容が直接呼び出し元のコードに挿入されていることがわかります。

IntelliJ IDEA を使ってバイトコードの表示・デコンパイル方法については以下に記載しています。

通常の関数

fun main(): Unit {
    val sum = adder(10, 5)
    println(sum)
}

fun adder(x: Int, y: Int) : Int{
    return x + y
}

デコンパイルした結果

// 一部抜粋

public final class MainKt {
   public static final void main() {
      int sum = adder(10, 5);     // 関数がコード通り呼び出されている
      System.out.println(sum);
   }

   public static void main(String[] var0) {
      main();
   }

   public static final int adder(int x, int y) {
      return x + y;
   }
}

インライン関数とした場合

fun main(): Unit {
    val sum = adder(10, 5)
    println(sum)
}

inline fun adder(x: Int, y: Int) : Int{
    return x + y
}

デコンパイルした結果

// 一部抜粋

public final class MainKt {
   public static final void main() {
      byte x$iv = 10;
      int y$iv = 5;
      int $i$f$adder = false;
      int sum = x$iv + y$iv;     // 関数の内容が展開されている。
      System.out.println(sum);
   }

   public static void main(String[] var0) {
      main();
   }

   public static final int adder(int x, int y) {
      int $i$f$adder = 0;
      return x + y;
   }
}