Erlang 高 CPU 使用率:フレームグラフで特定した PCRE 正規表現のボトルネック
Erlang アプリケーションの CPU 使用率が 200% を超える場合、ボトルネックは BEAM VM ランタイムの深部に隠れていることがよくあります。OpenResty XRay で実行中の Erlang プロセスをプロファイリングしたところ、Erlang の高 CPU 使用率の原因が check_resp_content 内の PCRE 正規表現バックトラッキング、具体的には Erlang ランタイムで PCRE ライブラリをラップする C 関数 erts_pcre_exec であることを特定しました。コード変更、再コンパイル、再起動は一切不要でした。
本ウォークスルーでは、top で CPU 使用率 200% 超の Erlang プロセスを観測するところから、Erlang レベルおよび C レベルのフレームグラフ分析を経て、原因となるソースファイルと行番号を正確に特定するまでの、完全なプロファイリングプロセスを示します。
Erlang プロセスの 200% 超 CPU 使用率の観測
Erlang の高 CPU 使用率は、通常 top や htop の出力で BEAM VM プロセスのビジー状態として現れます。プロセス名は beam.smp であることが多いですが、今回のケースのように、起動した escript の名前が表示される場合もあります。いずれにしても、重要なのはどの Erlang コードパスが原因かということです。
top コマンドを実行して、CPU 使用率を確認します。
このプロセスが CPU コアの 200% 以上を消費していることがわかります。
このプロセスは rebar3 という名前で、Erlang プロジェクトを管理およびビルドするためのツールです。ここではプロジェクトの起動に使用されています。
ps コマンドを実行して、このプロセスの詳細を確認します。
この rebar3 は Linux ディストリビューション標準の実行ファイルです。このプログラムは当然、標準のディストリビューションに付属の Erlang でコンパイルされています。
OpenResty XRay による Erlang CPU 使用率のプロファイリング
OpenResty XRay は、この未変更の Erlang プロセスを Erlang 言語レベルと BEAM VM の C レベルの両方でリアルタイムに分析できます。対象アプリケーションにモジュールやプラグインをインストールする必要はありません。Web コンソールで「Guided Analysis」に移動し、問題タイプとして「High CPU Usage」を選択します。次に、対象マシン上の Erlang アプリケーションを選択します。
CPU コアを 200% 近く消費しているプロセスを選択します。これは先ほど top で確認したものです。
OpenResty XRay は複数の言語レベルを同時に分析できます。ここでは Erlang と C/C++ の両方を選択したままにし、Erlang コードレベルと BEAM VM 内部の両方でホットスポットを確認します。最大分析時間はデフォルトの 300 秒のままにします。
分析を開始すると、システムは複数回の分析を継続的に実行します。この例では 2 回で十分です。
自動生成された分析レポートが表示されます。
Erlang レベル CPU フレームグラフ:check_resp_content における正規表現
最もホットな Erlang コードパス(第 1 位)を確認します。
check_resp_content 関数は、レスポンスのコンテンツをチェックするための業務ロジック関数です。
この関数は lists モジュールの filter 関数を呼び出してリストをフィルタリングしています。
フィルタリング条件は無名関数で、この無名関数内で正規表現マッチングを使用しています。これらの呼び出しはすべて check_resp_content 関数内で発生しています。
「More」をクリックして、この最もホットなコードパスの詳細を表示します。
このコードパスは、Erlang レベルの CPU フレームグラフから自動的に導き出されたものです。
先ほどのコードパスのより詳細な説明です。
先ほどのホットコードパスに戻ります。最初の関数の緑枠にマウスカーソルを合わせると、ツールチップにソースファイルのパスが表示されます。
Erlang ソースコードの行番号は 15 です。
アイコンをクリックしてソースファイルのパスをコピーします。
Vim エディタを使用して Erlang ソースファイルを開きます。任意のエディタをご使用いただけます。
OpenResty XRay の提案に従って、15 行目に移動します。
これは re モジュールの run 関数で、PCRE 正規表現エンジンへの標準的な Erlang インターフェースです。
これは先ほどレポートで確認した lists:filter 関数呼び出しです。
このコードは確かに check_resp_content 関数内にあります。正規表現を最適化して高コストのバックトラッキング操作を回避するか、バックトラッキングを行わない正規表現エンジンの使用を検討できます。
上位 2 つの Erlang コードパスは類似しており、どちらも正規表現マッチングを実行しています。
3 番目の Erlang コードパスは、マッチング前に入力文字列の長さを計算しています。
iolist はマッチング対象の入力文字列で、erts_iolist_size は PCRE エンジンに渡す前に文字列の長さを計算しています。
Content 変数がマッチング対象の入力文字列です。
C レベル分析:erts_pcre_exec と PCRE バックトラッキング
Web コンソールに戻ります。C レベル分析は BEAM VM 内部のパフォーマンスホットスポットを明らかにし、CPU ボトルネックが PCRE ライブラリの深部にまで達していることを確認します。
PCRE ライブラリの match 関数が、実際の正規表現マッチングを実行しています。
2 番目の関数 erts_pcre_exec は、Erlang ランタイムによる PCRE ライブラリの pcre_exec 関数のラッパーです。
Erlang の re モジュールの re_run 関数が、正規表現の実行に使用されています。
この 16 進数アドレスは、このコードパスが JIT コンパイルされた Erlang コード内で実行されていることを示しています。
BEAM VM における PCRE 正規表現バックトラッキングが高コストな理由
C レベルのフレームグラフにより、CPU 時間が erts_pcre_exec(BEAM VM に組み込まれた PCRE ライブラリへのバインディング)内で消費されていることが確認されました。Erlang の re モジュールはすべての正規表現処理を PCRE に委譲しており、PCRE はバックトラッキング NFA エンジンを使用しています。パターンに .*、.+、ネストされた選択肢などの量指定子が含まれている場合、エンジンはマッチが存在しないと結論づける前に、指数的な数のマッチパスを探索する可能性があります。これは壊滅的バックトラッキングとして知られています。
このような PCRE 正規表現のボトルネックを修正するには、正規表現エンジンによる高コストのバックトラッキング操作を回避するよう正規表現を最適化するか、バックトラッキングを行わない正規表現エンジンの使用を検討できます。
自動監視とレポート
本番環境のワークロードでは、OpenResty XRay が Erlang プロセスを継続的に監視し、Insights ページで日次および週次のレポートを自動生成します。手動のガイド付き分析は不要です。
FAQ:Erlang 高 CPU 使用率
Erlang プロセスの CPU 使用率が非常に高いのはなぜですか?
Erlang の高 CPU 使用率の一般的な原因は、ホットコードパスにおける高コストな操作です。正規表現マッチング、ガベージコレクションの負荷、非効率なリスト処理などが該当します。今回のケースでは、OpenResty XRay が CPU ボトルネックを check_resp_content 関数内の PCRE 正規表現バックトラッキングまで追跡しました。この関数では lists:filter/2 ループ内の各要素に対して re:run/2 が呼び出されていました。Erlang レベルと C レベル両方の CPU フレームグラフにより、erts_pcre_exec までの完全な呼び出しチェーンが明らかになりました。
Erlang の高 CPU の原因となるコードをどのように特定しますか?
OpenResty XRay のガイド付き分析を使用して、Erlang と C の両方の言語レベルで CPU フレームグラフを生成します。フレームグラフは関数呼び出しをスタック表示し、幅が CPU 時間を表します。関数のボックスにマウスカーソルを合わせるとソースファイルのパスと行番号が表示されるので、任意のエディタでそのファイルを開いて原因となるコードを調査できます。コード変更や再起動は不要です。OpenResty XRay は実行中の Erlang プロセスに直接アタッチします。
PCRE 正規表現パターンは Erlang で高 CPU の原因になりますか?
はい。Erlang の re モジュールは erts_pcre_exec を介して PCRE ライブラリに正規表現処理を委譲しており、PCRE はバックトラッキング NFA エンジンを使用しています。ネストされた量指定子や選択肢を含むパターンは壊滅的バックトラッキングを引き起こし、指数的な CPU 時間を消費する可能性があります。今回のケースでは、PCRE の match 関数が C レベルのフレームグラフで末端の CPU 消費元として表示され、正規表現のバックトラッキングが Erlang の高 CPU 使用率の根本原因であることが確認されました。
OpenResty XRay について
OpenResty XRay は動的トレーシング製品であり、実行中のアプリケーションを自動的に分析して、パフォーマンスの問題、動作の問題、セキュリティの脆弱性を診断し、実行可能な提案を提供いたします。基盤となる実装において、OpenResty XRay は弊社の Y 言語によって駆動され、Stap+、eBPF+、GDB、ODB など、様々な環境下で複数の異なるランタイムをサポートしております。
このチュートリアルを気に入っていただけましたら、ぜひ当ブログサイトおよび YouTube チャンネルのご登録をお願いいたします。ありがとうございます!
著者について
章亦春(Zhang Yichun)は、オープンソースの OpenResty® プロジェクトの創始者であり、OpenResty Inc. の CEO および創業者です。
章亦春(GitHub ID: agentzh)は中国江蘇省生まれで、現在は米国ベイエリアに在住しております。彼は中国における初期のオープンソース技術と文化の提唱者およびリーダーの一人であり、Cloudflare、Yahoo!、Alibaba など、国際的に有名なハイテク企業に勤務した経験があります。「エッジコンピューティング」、「動的トレーシング」、「機械プログラミング」 の先駆者であり、22 年以上のプログラミング経験と 16 年以上のオープンソース経験を持っております。世界中で 4000 万以上のドメイン名を持つユーザーを抱えるオープンソースプロジェクトのリーダーとして、彼は OpenResty® オープンソースプロジェクトをベースに、米国シリコンバレーの中心部にハイテク企業 OpenResty Inc. を設立いたしました。同社の主力製品である OpenResty XRay(動的トレーシング技術を利用した非侵入型の障害分析および排除ツール)と OpenResty Edge(マイクロサービスおよび分散トラフィックに最適化された多機能ゲートウェイソフトウェア)は、世界中の多くの上場企業および大企業から高い評価を得ております。OpenResty 以外にも、章亦春は Linux カーネル、Nginx、LuaJIT、GDB、SystemTap、LLVM、Perl など、複数のオープンソースプロジェクトに累計 100 万行以上のコードを寄与し、60 以上のオープンソースソフトウェアライブラリを執筆しております。
翻訳
英語版の原文と日本語訳版(本文)をご用意しております。読者の皆様による他の言語への翻訳版も歓迎いたします。全文翻訳で省略がなければ、採用を検討させていただきます。よろしくお願いいたします。




















































