Rescale での BYO ソフトウェア回帰テスト

 回帰テスト2
Rescale は、ソフトウェア ベンダーにとって貴重な回帰およびパフォーマンス テストのリソースです。 API とコマンド ライン ツールを使用して、Rescale で社内の回帰テストのすべてまたは一部を実行する方法について説明します。 Rescale でテストする利点は次のとおりです。

  1. コンピューティング リソースはオンデマンドなので、実際にテストを実行する場合にのみ料金が発生します。
  2. コンピューティング リソースもスケーラブルであるため、大規模なテスト スイートを並行して実行し、より早くフィードバックを得ることができます。
  3. 異種リソースを使用して、さまざまなハードウェア構成 (Infiniband、10GigE、Tesla、Phi コプロセッサなど) でソフトウェアのパフォーマンスをテストできます。
  4. Rescale でソフトウェアをテストすると、オプションで Rescale の他の顧客にプライベート ベータ リリースを提供できるようになります。

テストのセットアップ

この記事の残りの部分では、次のファイルのセットがあることを前提とします。

  1. 一般的にサポートされているアーカイブ形式 (tar.gz、zip など) でのソフトウェア パッケージの完全なビルド ツリー
  2. アーカイブされた参照テスト入力と予想される出力のセット
  3. XNUMX つ以上のテスト入力に対してソフトウェア ビルドを実行するためのスクリプトまたはコマンド ライン
  4. 実際のテスト出力を期待される出力で評価するスクリプト
  5. (オプション) 完全なビルド ツリーの上にオーバーレイする、より小規模な増分ビルド製品のセット

以下の例では、Python を使用します。 SDK。 以下のサンプルの一部は SDK リポジトリで入手できます。 こちら. SDK は REST API をラップするだけなので、で参照されているエンドポイントを使用してこれらの例を他の言語に移植できます。 https://github.com/rescale/python-sdk/blob/master/rescale/client.py.
これらすべての例では次のものが必要であることに注意してください。

  1. のアカウント リスケールプラットフォーム
  2. 地方 RESCALE_API_KEY メイン プラットフォーム ページの [設定] -> [API] にある API キーに設定された環境変数

単一のジョブからテストを実行する

最も単純な例から始めます。完全なビルドとテストの参照データをジョブ入力ファイルとしてアップロードし、テストを連続して実行し、結果を比較します。 アップロードして実行する「ソフトウェア」の例から始めましょう。 ソフトウェア パッケージとテスト ファイルのリストは次のとおりです。

echoware/bin/echo.sh (テストインからテストアウトまでをエコーするだけ) test[0-9]/in (テスト入力) test[0-9]/expected_out (予期されるテスト出力) test[0-9]/out (実際のテスト実行の出力)

各ソフトウェア ビルドとテスト ケースは個別にアーカイブされます。 テスト スイート ジョブを準備して実行する手順は次のとおりです。

  1. Rescale Python SDK を使用して、ビルド、参照テスト データ、結果比較スクリプトをアップロードします。
#!/usr/bin/env python3 import rescale.client TEST_ARCHIVE = 'inputs/all_tests.tar.gz' BUILD_ARCHIVE = 'inputs/echoware0.1.tar.gz' POST_COMPARE_SCRIPT = 'inputs/compare_results.sh' input_files = [rescale .client.RescaleFile(file_path=TEST_ARCHIVE), rescale.client.RescaleFile(file_path=BUILD_ARCHIVE), ] post_process_file = \ rescale.client.RescaleFile(file_path=POST_COMPARE_SCRIPT)

再スケールファイル ローカル ファイルの内容を Rescale にアップロードし、そのファイルを参照するメタデータを返します。 この時点で、これらのファイルは次の場所で表示できます。  https://www.rescale.com/route/files/.
      2. テスト スイート ジョブを作成します。

テストコマンド = """
テストケースの場合 $(find . -name "test[0-9]*" -type d); do ./echoware/bin/echo.sh $testcase 完了 """ POST_RUN_COMPARE_COMMAND = """ for テストケース in $(find . -name "test[0-9]*" -type d); do ./compare_results.sh $testcase 完了 """ JOB_NAME = 'echoware0.1-all-tests' job_definition = { 'name': JOB_NAME, 'isLowPriority': True, 'jobanalyses': [ { 'analyses': { ' code': 'custom' }, 'hardware': { 'coresPerSlot': 1, 'slots': 1, 'coreType': { 'code': 'standard-plus' } }, 'inputFiles': [{'id ': inp.id} for inp in input_files], 'command': TEST_COMMAND, 'postProcessScript': {'id': post_process_file.id}, 'postProcessScriptCommand': POST_RUN_COMPARE_COMMAND } ], } job = rescale.client.RescaleJob(json_data) =ジョブの定義)

リスケールジョブ 新しいジョブが作成され、次の場所で表示できるようになります。 https://www.rescale.com/route/jobs/。 ここでは単一の Marble コアでジョブを実行していることに注意してください。 増やすことで、より多くのコアを実行することを選択できます スロットあたりのコア数 または、別のコア タイプ コードを選択してコア タイプを変更します。 RescaleConnect.get_core_types().
なお、  command & postProcessScriptコマンド フィールドには有効な bash スクリプトを使用できるため、テストの実行方法と結果の評価方法にかなりの柔軟性があります。 この非常に単純な例では、テスト後のコマンドの比較は単に差分を行うだけです。 でる & 予想外 各テスト ケース ディレクトリ内のファイル。

  1. ジョブを実行のために送信し、完了するまで待ちます。
job.submit() job.wait()

ジョブ クラスターがプロビジョニングされると、入力ファイルはクラスターに転送され、暗号化されず、作業ディレクトリ内で圧縮されません。 次に、 テストコマンド が実行され、続いて POST_RUN_COMPARE_COMMAND.

  1. テスト結果をダウンロードします。 すべての Rescale ジョブ コマンドは stdout にリダイレクトされます。 process_output.log その XNUMX つのファイルをダウンロードして、テスト結果の概要を取得しましょう。
STDOUT_LOG = 'process_output.log' test_log = job.get_file(STDOUT_LOG) test_log.download(target=test_log.name)

ここで、ジョブの後処理ステップとしてテスト結果の比較を行うことで、テスト結果を取得するまでにかかる時間が遅れる可能性のある大きな出力ファイルのダウンロードを回避できることに注意することが重要です。 これでは、実際の出力と同様のサイズになることが多いテスト参照出力をアップロードする必要があるという問題は解決されていません。 重要なのは、起動するすべてのテストジョブではなく、ファイルが変更されたときにのみ Rescale にファイルをアップロードする必要があるということです。 参照テスト ケースが頻繁に変更されないと仮定すると、Rescale にアップロードしたファイルを後のテスト実行で再利用できるようになりました。
この例の全文を見つけることができます こちら.

リファレンステストデータの再利用

ここで、送信されたテスト ジョブごとに参照テスト データをアップロードしないように、上記の手順を変更します。

  1. (変更) Rescale でテスト ファイルのメタデータを検索し、後続のジョブへの入力ファイルとして使用します。
# 上記のvar設定を省略original_test_file = rescale.client.RescaleFile.get_newest_by_name(TEST_ARCHIVE) input_files = [original_test_file, rescale.client.RescaleFile(file_path=BUILD_ARCHIVE), ] post_process_file = \ rescale.client.RescaleFile.get_newest_by_name(POST_COMPARE_SCRIPT)

RescaleFile.get_newest_by_name は、すでに Rescale にアップロードされたテスト ファイルのメタデータを取得するだけです。 同じ名前で複数のテスト アーカイブをアップロードした場合は、最後にアップロードしたものが選択されることに注意してください。
ステップ 2 ~ 4 は前の例と同じです。

長時間実行されるテストを並列化する

前の例では、すべてのテストを順番に実行するだけでしたが、いくつかのテストを並行して実行してみましょう。 この例では、テストが「短い」テストと「長い」テストに分割されていると仮定します。 短いテストは、というアーカイブにあります。 all_short_tests.tar.gz そして、それぞれの長いテストは、と呼ばれる別のアーカイブにあります。 long_test_.tar.gz.
ここで、すべての短いテストに対して XNUMX つのジョブを起動し、長いテストに対してはテストごとに XNUMX つのジョブを起動します。 最初の例で行ったように、これらのテスト ファイルはすでに Rescale にアップロードされていると仮定します。

# コマンド変数の設定を省略 SHORT_TEST_ARCHIVE = 'inputs/all_short_tests.tar.gz' LONG_TEST_FORMAT = 'inputs/long_test_{i}.tar.gz' LONG_TEST_COUNT = 10 BUILD_ARCHIVE = 'inputs/echoware0.1.tar.gz' POST_COMPARE_SCRIPT = 'inputs /compare_results.sh' # ローカル コピーからアップロードするのではなく、Rescale で名前で検索 short_test_bundle = \ rescale.client.RescaleFile.get_newest_by_name(SHORT_TEST_ARCHIVE) long_test_inputs = [ rescale.client.RescaleFile.get_newest_by_name(LONG_TEST_FORMAT.format(i=i)) for i in range(LONG_TEST_COUNT): post_process_file = \ rescale.client.RescaleFile.get_newest_by_name(POST_COMPARE_SCRIPT) # ローカル コピーをアップロード build_input = rescale.client.RescaleFile(file_path=BUILD_ARCHIVE) def create_job(name, test_input, core_type, core_count): input_files = [build_input, test_input] job_setting = { 'name': 名前, 'isLowPriority': True, 'jobanalyses': [ { 'analyses': { 'code': 'custom' }, 'hardware': { 'coresPerSlot': core_count、'slots': 1、'coreType': { 'code': core_type } }、'inputFiles': [{'id': inp.id} for inp in input_files]、'command': TEST_COMMAND、'postProcessScript' : {'id': post_process_file.id}, 'postProcessScriptCommand': POST_RUN_COMPARE_COMMAND } ], } return rescale.client.RescaleJob(json_data=job_diction) # すべてのテスト ジョブを作成 short_test_job = create_job('echoware0.1-all-short-tests '、short_test_bundle、'standard-plus'、1) long_test_jobs = [create_job('echoware0.1-long-test-{0}'.format(i)、long_test、'hpc-plus'、32) for i、long_test in enumerate(long_test_inputs)] test_jobs = [short_test_job] + long_test_jobs # すべて送信

[test_jobs のジョブの job.submit()]

# すべてが完了するまで待ちます

[test_jobs のジョブの job.wait()]

# test_jobs のジョブの結果を取得 [job.get_file(STDOUT_LOG).download(target='{0}.out'.format(job.name))]

この例では、単一の Marble コアを使用して短いテスト ジョブを開始し、32 コア (2 ノード) のニッケル MPI クラスターを使用してそれぞれの長いテストを開始しました。
このテスト ジョブ構成は、パフォーマンス テストに特に適しています。 特定のビルドとテスト ケースの組み合わせがスケールするかどうかをテストするには、それぞれ 4、1、2、4 ノードを持つ 8 つのジョブを起動します。
この例は次のとおりです。 こちら.

インクリメンタルビルド

上記では、Rescale に既に保存されている同じデータを再利用することで、テスト実行ごとにテスト データを再アップロードすることを回避しました。 テストする必要がある大規模なソフトウェア ビルドがある場合は、アップロード済みのデータも再利用したいと考えますが、通常、テストされる各ビルドは異なります。 ただし、多くの場合、パッケージ全体のファイルの小さなサブセットのみがビルドごとに変更されます。
ビルドの類似性を活用するために、最初のジョブでアップロードしたベース ビルド ツリーの上に圧縮されていない増分ビルド デルタを提供できます。 要件は 2 つだけです。

  1. ビルド デルタはベース ビルドと同じディレクトリ構造を持つ必要があります
  2. ベース ビルド アーカイブの後に、入力ファイルとしてビルド デルタ アーカイブを指定する必要があります。
この変更の抜粋は次のとおりです: FULL_BUILD_ARCHIVE = 'inputs/echoware0.1.tar.gz' BUILD_DELTA = 'inputs/echoware0.2-delta.tar.gz' # Rescale で名前で検索base_build_input = \ rescale.client.RescaleFile .get_newest_by_name(FULL_BUILD_ARCHIVE) # ローカル コピーをアップロード incremental_build_input = rescale.client.RescaleFile(file_path=BUILD_DELTA) def create_job(name, test_input, core_type, core_count): input_files = [base_build_input, test_input, incremental_build_input] job_definition = { 'name': JOB_名前, 'isLowPriority': True, 'jobanalyses': [ { 'analyses': { 'code': 'custom' }, 'hardware': { 'coresPerSlot': 1, 'slots': core_count, 'coreType': { ' code': core_type } }、'inputFiles': [{'id': inp.id} for inp in input_files]、'command': TEST_COMMAND、'postProcessScript': {'id': post_process_file.id}、'postProcessScriptCommand' : POST_RUN_COMPARE_COMMAND } ], } return rescale.client.RescaleJob(json_data=job_writing)

上記では、 ベースビルド入力 すでに Rescale 上にあるファイルからのものであり、 インクリメンタルビルド入力 が毎回アップロードされます。

実験計画法 (DOE) ジョブの並列処理

テストを実行するもう XNUMX つの方法は、複数のテストを XNUMX つの DOE ジョブにグループ化することです。 並行して実行できるテストの数は、ジョブに対して構成したタスク スロットの数によって定義されます。 次に、「」で説明されているように、テンプレート化された構成ファイルによってパラメータ化できるようにテスト実行を構造化します。 https://www.rescale.com/resources/getting-started/doe/.
この方法には、複数ジョブの場合と比較して、ジョブ クラスターのセットアップ時間が短縮されるという利点があります。 欠点は、各テストの実行が、タスク スロットに対して定義したのと同じハードウェア構成に制限されることです。 Python SDK を使用して DOE ジョブを設定する方法の例については、を参照してください。 https://github.com/rescale/python-sdk/tree/master/examples/doe.

大きなファイルのアップロード

上記の例では、単純な PUT リクエストを使用して入力ファイルをアップロードしました。 これは遅くなり、複数ギガバイトのファイルでは機能しないことがよくあります。 別の方法として、Rescale CLI ツールを使用することもできます。このツールは、帯域幅が最適化されたファイルのアップロードとダウンロードを提供し、転送が中断された場合に転送を再開できます。
Rescale CLI の詳細については、ここを参照してください。 .
Rescale でテストを実行することは、大規模な回帰およびパフォーマンス テスト スイートのテスト時間を短縮し、内部コンピューティング リソースへの負担を軽減する優れた方法です。 Rescale API は、大容量メモリ、大容量ストレージ、Infiniband、GPU 対応クラスターなどのさまざまなハードウェア構成でテストのグループを起動するための非常に柔軟な方法を提供します。 Rescale で独自のテストを行うことに興味がある場合は、次の URL で SDK とサンプル スクリプトを確認してください。 https://github.com/rescale/python-sdk をご覧いただくか、 .

著者

  • マーク·ホイットニー

    Mark Whitney は、Rescale のエンジニアリング ディレクターです。 彼の専門分野には、ハイ パフォーマンス コンピューティング アーキテクチャ、量子情報研究、クラウド コンピューティングが含まれます。 彼はカリフォルニア大学バークレー校でコンピューター サイエンスの博士号を取得しています。

類似の投稿