LuaJITとは何か?APISIXがLuaJITを選ぶ理由

Tao Yang

April 14, 2023

Products

APIゲートウェイのレベルを次の段階に引き上げる準備はできていますか?それなら、Apache APISIXに注目する価値があります。Apache APISIXは、開発者コミュニティで話題を集めているクラウドネイティブのAPIゲートウェイです。さらに興味深いのは、Apache APISIXが主にLuaJITを使用して構築されていることです。LuaJITは、軽量で効率的な言語であり、他の有名な言語ほど知られていません。

この記事では、Apache APISIXがより人気のある言語や技術ではなくLuaJITを選んだ理由と、LuaJITのユニークな機能と利点がどのようにして最も要求の厳しいワークロードにも対応できる高速なAPIゲートウェイの構築に役立つのかを詳しく見ていきます。APIゲートウェイを強化したいと考えている方は、ぜひ読み続けてください!

LuaJITとは

定義

簡単に言うと、LuaJITはLuaプログラミング言語のJust-In-Time(JIT)コンパイラの実装です。LuaJITに馴染みのない読者のために、LuaとJITの2つの部分に分けて説明します。

Lua

Luaは、エレガントで学びやすいプログラミング言語であり、自動メモリ管理、完全なレキシカルスコープ、クロージャ、イテレータ、コルーチン、適切な末尾呼び出し、連想配列を使用した実用的なデータ処理などの特徴を持っています。Luaの構文についてさらに学びたい方は、Getting Started With Luaを読むことをお勧めします。

Luaは、C言語や他の広く使用されているプログラミング言語と簡単に統合できるように設計されており、開発者がそれらの言語の強みを活用できるようにしています。Luaは、C言語のような言語では通常強みとされない機能、例えばハードウェアに対する高レベルの抽象化、動的な構造、簡単なテストなどを提供します。その小さな言語カーネルとANSI C標準への依存により、異なるプラットフォーム間で高い移植性を持っています。その結果、Luaはスタンドアロンプログラムとして実行できるスクリプト言語であるだけでなく、他のアプリケーションに組み込むことができる組み込み言語でもあります。

Apache APISIXは、低レベルでLuaとCの両方を使用する優れた例です

しかし、この時点で、Luaには従来のスクリプト言語に見られる2つの一般的な問題がありました:効率の低さとコードの露出です。LuaJITが導入したJIT技術は、これらの2つの問題を効果的に解決できます。

JIT

JIT(Just-In-Time Compilation)は、動的コンパイルの一種です。動的コンパイルは、コンピュータサイエンスにおける唯一のコンパイル形式ではありません。例えば、広く使用されているC言語は、静的コンパイルと呼ばれる異なる形式のコンパイルを使用しています。

注意すべき点は、C言語で使用される動的コンパイルの反対としてAhead-of-Time Compilation(AOT)という用語を使用することが多いですが、これらは完全に等価ではありません。AOTは、プログラムを実行する前に「高レベル」言語を「低レベル」言語にコンパイルする動作を説明するだけです。そのコンパイルのターゲット言語は、必ずしもプログラムホストマシン固有のマシンコードである必要はなく、任意に定義されます。例えば、JavaをCにコンパイルしたり、JavaScriptをV8にコンパイルしたりすることもAOTと見なされます。すべての静的コンパイルは技術的には事前に実行されるため、この特定の文脈では、AOTはJITに対する静的コンパイルの反対と見なすことができます。

これらの複雑な用語を脇に置いて、静的コンパイルの出力を考えると、Lua言語が直面する問題も静的コンパイルによって解決できることがわかります。しかし、これにより、Luaがスクリプト言語として提供する利点、つまりホットアップデートの柔軟性と優れたプラットフォーム互換性が失われます。そのため、現在、特別な要件を持つものを除いて、ほとんどのスクリプト言語はJITを使用して言語のパフォーマンスを向上させようとしています。例えば、Chromiumプラットフォーム上のV8のJavaScriptや、YJITを使用するRubyなどです。

JITは、Luaの動的インタプリテーションとCの静的コンパイルの利点と欠点を組み合わせようとします。スクリプト言語の実行中、JITは実行されているコードフラグメントを継続的に分析し、それらをコンパイルまたは再コンパイルして実行効率を向上させます。この時点で、JITの背後にある仮定は、このプロセスから得られるパフォーマンスの向上が、コードのコンパイルまたは再コンパイルのコストを上回るということです。理論的には、動的に再コンパイルできるため、JITは実行中のプログラムが基づく特定のプラットフォームアーキテクチャに対して最適化および高速化を行い、場合によっては静的コンパイルよりも高速な実行速度を実現できます。

JITは、従来のMethod JITと、現在LuaJITが使用しているTrace JITの2つのタイプに分けられます。Method JITは各メソッドをマシンコードに変換しますが、より進化したTrace JITは、「1回または2回しか実行されないコードの場合、インタプリテーション実行はJITコンパイル実行よりも高速である」と仮定します。これに基づいて、Trace JITは従来のJITを最適化し、頻繁に実行されるコードフラグメント(つまり、ホットパス上のコード)をトレースする必要があるコードとして識別し、この部分のコードをマシンコードにコンパイルして実行します。以下の図に示すように。

LuaJITの原理

LuaJIT

LuaJIT(バージョン2.x)は、アセンブリ言語で書かれた高速インタプリタと、Static Single Assignment (SSA)に基づく最適化されたコードジェネレータバックエンドを統合することで、JITのパフォーマンスを大幅に向上させました。その結果、LuaJITは最も高速な動的言語実装の1つになりました。

さらに、ネイティブLuaでのCとのやり取りにおける煩雑なバインディングと比較して、LuaJITはFFI(Foreign Function Interface)も実装しています。この技術により、Luaコードから外部のC関数を呼び出したり、Cデータ構造を直接使用したりすることができます。パラメータの数や型を知る必要はありません。この機能により、パフォーマンスが重要なシナリオでは、ネイティブLuaのTable型ではなく、FFIを使用して必要なデータ構造を直接実装することができ、プログラムのパフォーマンスをさらに向上させることができます。FFIを使用してパフォーマンスを向上させる技術については、この記事の範囲を超えていますが、より詳細な情報はWhy Does lua-resty-core Perform Better?の記事で見つけることができます。

まとめると、LuaJITは、Lua構文を使用して、これまでで最も高速なTrace JITの1つを実装しています。さらに、FFIなどの機能を提供することで、Luaの効率の低さとコードの露出の問題を解決し、Luaを非常に柔軟で高性能、超低メモリ使用量のスクリプトおよび組み込み言語にしています。

WASMや他の言語との比較

LuaやLuaJITと比較して、私たちは他の言語、例えばJavaScript(Node.js)、Python、Golang、Javaなどに慣れているかもしれません。Lua/LuaJITとこれらの人気のある言語を比較することで、LuaJITのユニークな機能と利点をよりよく理解することができます。以下にいくつかの簡単な比較を示します:

  • Luaの構文は、ソフトウェアエンジニア以外の人向けに設計されています。R言語と同様に、Luaも配列のインデックスが1から始まるため、一般の人に適しています。
  • Luaは組み込み言語として非常に適しています。Lua自体には軽量なVMがあり、LuaJITはさまざまな機能と最適化を追加しても軽量です。そのため、LuaJITはCプログラムに直接統合しても、Node.jsやPythonのような大きなランタイム環境と比較してサイズが大幅に増加しません。したがって、Luaは実際にはすべての組み込み言語の中で最も広く使用され、主流の選択肢です。
  • Luaは「接着剤」言語としても非常に適しています。JavaScript(Node.js)やPythonと同様に、Luaも異なるライブラリやコードを非常にうまく接続できます。ただし、他の言語とは少し異なり、Luaは基盤となるエコシステムとの結合度が高いため、Luaのエコシステムは異なる分野で普遍的ではないかもしれません。

WASM(Web Assembly)は、新興のクロスプラットフォーム技術です。この技術は、当初JavaScriptを補完するために設計されましたが、他の言語をWASMバイトコードにコンパイルし、セキュアなサンドボックスとしてコードを実行できるため、ますます多くのプログラムがWASMを組み込みまたは「接着剤」プラットフォームとして使用することを検討しています。それでも、Lua/LuaJITは新興のWASMと比較して多くの利点を持っています:

  • WASMのパフォーマンスは限られており、アセンブリのレベルには達しません。一般的なシナリオでは、WASMは確かにLuaよりもパフォーマンスが優れていますが、WASMとLuaJITの間にはまだギャップがあります。
  • WASMとホストプログラム間のデータ伝送効率は比較的低いです。一方、LuaJITはFFIを通じて効率的なデータ伝送を行うことができます。

なぜApache APISIXはLuaJITを選んだのか?

上記でLuaJITの多くの利点を説明しましたが、Luaは人気のある言語でもなく、ほとんどの開発者にとって人気のある選択肢でもありません。では、なぜApache FoundationのクラウドネイティブAPIゲートウェイであるApache APISIXはLuaJITを選んだのでしょうか?

クラウドネイティブAPIゲートウェイとして、Apache APISIXは動的、リアルタイム、高性能という特徴を持ち、ロードバランシング、動的アップストリーム、カナリアリリース、サービスデグレード、認証可観測性などの豊富なトラフィック管理機能を提供します。Apache APISIXを使用して、従来の南北トラフィックだけでなく、サービス間の東西トラフィックも処理でき、k8sのIngressコントローラーとしても機能します。

これらすべては、Apache APISIXが選択したNGINXとLuaJITの技術スタックに基づいて構築されています。

LuaJITとNGINXを組み合わせる利点

NGINXは、高性能なウェブサーバーとして知られており、HTTP、TCP/UDPプロキシおよびリバースプロキシとして機能します。

しかし、実際には、NGINXの設定ファイルを変更するたびに、nginx -s reloadコマンドを使用してNGINXの設定をリロードする必要があることが煩わしいと感じることがあります。

さらに、このコマンドを頻繁に使用して設定をリロードすると、接続の不安定性を引き起こし、ビジネスの損失の可能性を高めることがあります。場合によっては、NGINXの設定リロードメカニズムが古いプロセスの回収に時間がかかりすぎて、通常のビジネス操作に影響を与えることもあります。このトピックについてより詳細な分析を読みたい方は、Why NGINX's reload is not a hot reload?の記事を読むことをお勧めします。ここではこのトピックについてさらに詳しく掘り下げません。

Apache APISIXの目的の1つは、NGINXの動的設定問題を解決することです。LuaJITの高い柔軟性、高性能、超低メモリ使用量により、これが可能になります。最も一般的なルートを例にとると、Apache APISIXはNGINXの設定ファイルに単一のlocationをメインエントリーポイントとして設定し、その後のルート分散はAPISIXのルート分散モジュールによって完了されます。これにより、ルートの動的設定が実現されます。

高性能を実現するために、Apache APISIXはC言語で書かれたプレフィックスツリーベースのルートマッチングアルゴリズムを使用し、その上でLuaJITが提供するFFIを使用してLua用のインターフェースを提供します。Luaの柔軟性により、Apache APISIXのルート分散モジュールは、特定の式やその他の方法を使用して、同じプレフィックスの下位ルートのマッチングを簡単にサポートできます。最終的に、NGINXのネイティブルート分散機能を置き換えることで、高性能で柔軟な動的設定機能を実現します。この機能の詳細な実装については、lua-resty-radixtreeroute.luaを参照してください。

ルーティングに加えて、APISIXは、サーバーを再起動することなく、バランシング、ヘルスチェック、アップストリームノードの設定、サーバー証明書、およびAPISIXの機能を拡張するプラグインなどの機能をリロードすることもできます。

さらに、LuaJITを使用してプラグインやその他の機能を開発するだけでなく、Apache APISIXはJava、Go、Node、Python、WASMなどのさまざまな言語を使用してプラグインを開発することもサポートしています。これにより、Apache APISIXのカスタム開発のハードルが大幅に下がり、豊富なプラグインエコシステムと活発なオープンソースコミュニティが形成されています。

Apache APISIXのプラグイン原理とエコシステム

結論

LuaJITは、LuaのJust-In-Timeコンパイラの実装です。

動的、リアルタイム、高性能なオープンソースAPIゲートウェイであるApache APISIXは、NGINXとLuaJITがもたらす高性能と高い柔軟性に基づいて、ロードバランシング、動的アップストリーム、カナリアリリース、サーキットブレーカー、認証、可観測性などの豊富なトラフィック管理機能を提供します。

現在、Apache APISIXは新しいバージョン3.xをリリースしており、オープンソースプロジェクトやクラウドプロバイダーとのさらなる統合、ネイティブgRPCサポート、追加のプラグイン開発オプション、サービスメッシュサポートなどが含まれています。Apache APISIXコミュニティに参加して、クラウドネイティブAPIゲートウェイにおけるLuaJITの応用についてさらに学びましょう。

Tags: