JMHを使用してマイクロサービスパイプラインのベンチマークを行った方法

JMHを使用してマイクロサービスパイプラインのベンチマークを行った方法

LogicMonitorでは、パフォーマンスとスケーラビリティに関してプラットフォームを継続的に改善しています。 LogicMonitorプラットフォームの重要な機能のXNUMXつは、生の出力で利用できないデータを使用して、監視対象システムから返されたデータを後処理する機能です。 複雑なデータポイント.

複雑なデータポイントは、生データの収集後にLogicMonitor自体によって計算されるため、LogicMonitorのメトリック処理パイプラインの中で最も計算量の多い部分のXNUMXつです。 したがって、メトリック処理パイプラインを改善する際に、複雑なデータポイントの計算のパフォーマンスをベンチマークして、拡張時にインフラストラクチャとアーキテクチャのデータ駆動型キャパシティプランニングを実行できるようにすることが重要です。

LogicMonitorマイクロサービステクノロジースタック  

LogicMonitorのメトリックパイプラインは、環境内でQuarkusの概念実証を構築したものであり、次のテクノロジースタックにデプロイされています。

  • Java 11(corretto、cuzライセンス)
  • Kafka(AWS MSKで管理) 
  • Kubernetes 
  • Nginx(Kubernetes内の入力コントローラー)
LogicMonitorKubernetesクラスター

なぜJMHを使用したのですか?

JMH (Java Microbenchmark Harness)は、JVMでベンチマークを作成するためのライブラリであり、OpenJDKプロジェクトの一部として開発されました。 これは、JVMを対象とするJavaおよびその他の言語で記述された、多くのユニットでのパフォーマンスとスループットのベンチマークを構築、実行、および分析するためのJavaハーネスです。 JMHを選択したのは、次の機能を提供するためです(これは完全なリストではありません)。

  • 測定が実際に開始される前に、最初に指定された数の「ウォームアップ」反復を実行するようにベンチマークされるコードを構成できます。 これにより、実際にベンチマークを行う前に、JVMの最適化を実行できます。
  • ベンチマークのためにコードが実行される反復回数を構成できます。
  • 測定したいものを設定できます。 使用可能なオプションには、スループット、平均時間、サンプル時間、および単一実行時間が含まれます。
  • ベンチマークの実行中にガベージコレクションを実行できるようにJMHを構成できます。
  • ベンチマークの実行中にJVMランタイム引数を渡すようにJMHを構成できます。たとえば、すべての異なるJVMメモリオプションを指定できます。
  • ベンチマークのレポートをファイルに永続化するようにJMHを構成できます。

計算集約型コードをどのようにベンチマークしましたか?

計算量の多いコードをベンチマークするための最初のステップは、他の依存関係をベンチマーク結果に織り込むことなく、ベンチマークハーネスで実行できるように、コードを分離することです。 この目的のために、最初に、複雑なデータポイントの計算を行うコードを独自のメソッドにリファクタリングして、他のコンポーネントを必要とせずに単独で呼び出すことができるようにしました。 これには、コードをモジュール化し、その保守性を向上させるという追加の利点もありました。 

ベンチマークを実行するために、JMHバージョン1.23との統合を使用しました JUnit。 最初に、いくつかのテストデータを使用して複雑なデータポイント計算を呼び出すメソッドを作成しました。 次に、このメソッドに@Benchmarkアノテーションを付けて、このメソッドをベンチマークする必要があることをJMHに通知しました。 次に、ベンチマークを実行する構成でJMHベンチマークランナーを実際に構成しました。 これには以下が含まれます:

  • 使用したいウォームアップ反復の数。
  • 使用したい測定の反復回数。
  • 実際に何を測定したいですか? この目的のために、これをスループットに設定します。

JVMメモリ引数を指定するオプションもありましたが、この場合、ベンチマーク対象のコードは計算量が多く、メモリ量が多くないため、それをやめることにしました。

最後に、JMHベンチマークランナーメソッドに@Testアノテーションを付けて、JUnitテストランナーを活用してJMHベンチマークを実行および実行できるようにしました。

JMHベンチマークランナーメソッドは次のようになります。

  @テスト
   public void executeJmhRunner()は例外をスローします{
       オプションjmhRunnerOptions = new OptionsBuilder()
           .include(“ \\。” + this.getClass()。getSimpleName()+“ \\。”)
           .warmupIterations(5)
           .measurementIterations(10)
           .forks(0)
           .threads(1)
           .mode(Mode.AverageTime)
           .shouldDoGC(true)
           .shouldFailOnError(true)
           .resultFormat(ResultFormatType.JSON)
           .result(“ / dev / null”)
           .shouldFailOnError(true)
           .jvmArgs( "-server")
           .build();
       new Runner(jmhRunnerOptions).run();
   }

何が見つかりましたか?

さまざまなサイズの入力データに対してベンチマークを実行し、スループットと入力データのサイズを記録しました。 この結果から、システム内の個々のノードの計算能力を特定することができ、そこから、計算量の多い負荷を持つ顧客向けにインフラストラクチャの自動スケーリングを計画することができました。 

LogicMonitorに計算負荷の高い顧客向けの自動スケーリングインフラストラクチャ
インスタンスあたりの複雑なデータポイントの数平均時間(ミリ秒単位)
102
153
205
257
3211