スループット・ベンチマークのサンプル#

このサンプルでは、スループット・モードで非同期推論要求 API を使用してモデルのパフォーマンスを推定する方法を示します。デモとは異なり、このサンプルには他の構成可能なコマンドライン引数がありません。サンプルのソースコードを変更して、さまざまなオプションを試してください。

レポートされる結果は、benchmark_app のレポートと異なる場合があります。一例として、コンピューター・ビジョン・タスクのモデル入力精度があります。 Benchmark_app は uint8 を設定しますが、サンプルではデフォルトのモデル精度 (通常は float32) を使用します。

サンプルを使用する前に、次の要件を参照してください:

  • このサンプルは、core.read_model でサポートされるすべてのファイル形式を受け入れます。

  • サンプルは、yolo-v3-tfface-detection-0200 モデルで検証されています。

  • サンプルをビルドするには、 “サンプルの導入” ガイドのサンプル・アプリケーションのビルドセクションにある手順を参照してください。

どのように動作するか#

このサンプルは、指定されたデバイスのモデルをコンパイルし、入力データをランダムに生成して、指定された秒数の間、非同期推論を複数回実行します。次に、パフォーマンス結果を処理してレポートします。

#!/usr/bin/env python3 
# -*- coding: utf-8 -*- 
# Copyright (C) 2022 Intel Corporation 
# SPDX-License-Identifier: Apache-2.0 

import logging as log 
import sys 
import statistics 
from time import perf_counter 

import numpy as np 
import openvino as ov 
from openvino.runtime import get_version 
from openvino.runtime.utils.types import get_dtype 

def fill_tensor_random(tensor): 
    dtype = get_dtype(tensor.element_type) 
    rand_min, rand_max = (0, 1) if dtype == bool else (np.iinfo(np.uint8).min, np.iinfo(np.uint8).max) 
    # np.random.uniform は high を除外します。生成するには 1 を追加します。 
    if np.dtype(dtype).kind in ['i', 'u', 'b']: 
        rand_max += 1 
    rs = np.random.RandomState(np.random.MT19937(np.random.SeedSequence(0))) 
    if 0 == tensor.get_size(): 
        raise RuntimeError("Models with dynamic shapes aren't supported.Input tensors must have specific shapes before inference") 
    tensor.data[:]= rs.uniform(rand_min, rand_max, list(tensor.shape)).astype(dtype) 

def main(): 
    log.basicConfig(format='[ %(levelname)s ] %(message)s', level=log.INFO, stream=sys.stdout) 
    log.info('OpenVINO:') 
    log.info(f"{'Build ':.<39} {get_version()}") 
    device_name = 'CPU’ 
    if len(sys.argv) == 3: 
        device_name = sys.argv[2] 
    elif len(sys.argv) != 2: 
        log.info(f'Usage: {sys.argv[0]} <path_to_model> <device_name>(default: CPU)') 
        return 1 
    # スループットを最適化。複数の openvino.runtime.InferRequest インスタンスを 
    # 非同期に実行すると、最高のスループットに達することができます 
    tput = {'PERFORMANCE_HINT': 'THROUGHPUT'} 
    # コアを作成してそれを使用してモデルをコンパイル。
    # CLI の 2 番目のパラメーターとして名前を指定してデバイスを選択。
    # AUTO デバイスでは CUMULATIVE_THROUGHPUT を PERFORMANCE_HINT として設定することが可能です 
    core = ov.Core() 
    compiled_model = core.compile_model(sys.argv[1], device_name, tput) 
    # AsyncInferQueue は最適な数の InferRequest インスタンスを作成 
    ireqs = ov.AsyncInferQueue(compiled_model) 
    # ireqs の入力データを入力 
    for ireq in ireqs: 
        for model_input in compiled_model.inputs: 
            fill_tensor_random(ireq.get_tensor(model_input)) 
    # ワークアップ 
    for _ in range(len(ireqs)): 
        ireqs.start_async() 
    ireqs.wait_all() 
    # seconds_to_run 秒と少なくとも niter 反復のベンチマーク 
    seconds_to_run = 10 
    niter = 10 
    latencies = [] 
    in_fly = set() 
    start = perf_counter() 
    time_point_to_finish = start + seconds_to_run 
    while perf_counter() < time_point_to_finish or len(latencies) + len(in_fly) < niter: 
        idle_id = ireqs.get_idle_request_id() 
        if idle_id in in_fly: 
            latencies.append(ireqs[idle_id].latency) 
        else: 
            in_fly.add(idle_id) 
        ireqs.start_async() 
    ireqs.wait_all() 
    duration = perf_counter() - start 
    for infer_request_id in in_fly: 
        latencies.append(ireqs[infer_request_id].latency) 
    # 結果をレポート 
    fps = len(latencies) / duration 
    log.info(f'Count: {len(latencies)} iterations') 
    log.info(f'Duration: {duration * 1e3:.2f} ms') 
    log.info('Latency:') 
    log.info(f' Median: {statistics.median(latencies):.2f} ms') 
    log.info(f' Average: {sum(latencies) / len(latencies):.2f} ms') 
    log.info(f' Min: {min(latencies):.2f} ms') 
    log.info(f' Max: {max(latencies):.2f} ms') 
    log.info(f'Throughput: {fps:.2f} FPS') 

if __name__ == '__main__': 
    main()
// Copyright (C) 2022 Intel Corporation 
// SPDX-License-Identifier: Apache-2.0 
// 

#include <algorithm> 
#include <condition_variable> 
#include <string> 
#include <vector> 

// clang-format off 
#include "openvino/openvino.hpp" 

#include "samples/args_helper.hpp" 
#include "samples/common.hpp" 
#include "samples/latency_metrics.hpp" 
#include "samples/slog.hpp" 
// clang-format on 

using Ms = std::chrono::duration<double, std::ratio<1, 1000>>; 

int main(int argc, char* argv[]) { 
    try { 
        slog::info << "OpenVINO:" << slog::endl; 
        slog::info << ov::get_openvino_version(); 

        std::string device_name = "CPU"; 
        if (argc == 3) { 
            device_name = argv[2]; 
        } else if (argc != 2) { 
            slog::info << "Usage : " << argv[0] << " <path_to_model> <device_name>(default: CPU)" << slog::endl; 
            return EXIT_FAILURE; 
        } 
        // スループットを最適化。Best throughput can be reached by 
        // 複数の::Infer Request インスタンスを非同期的に実行 
        ov::AnyMap tput{{ov::hint::performance_mode.name(), ov::hint::PerformanceMode::THROUGHPUT}}; 

        // ov::Core を作成しそれを使用してモデルをコンパイル 
        // CLI の 2 番目のパラメーターとして名前を指定してデバイスを選択。 
        // AUTO デバイスでは、ov::hint::PerformanceMode として CUMULATIVE_THROUGHPUT を設定できます。 
        ov::Core core; 
        ov::CompiledModel compiled_model = core.compile_model(argv[1], device_name, tput); 
        // ov::InferRequest インスタンスの最適な数を作成 
        uint32_t nireq = compiled_model.get_property(ov::optimal_number_of_infer_requests); 
        std::vector<ov::InferRequest> ireqs(nireq); 
        std::generate(ireqs.begin(), ireqs.end(), [&] { 
            return compiled_model.create_infer_request(); 
        }); 
        // ireqs の入力データを入力 
        for (ov::InferRequest& ireq : ireqs) { 
            for (const ov::Output<const ov::Node>& model_input : compiled_model.inputs()) { 
                fill_tensor_random(ireq.get_tensor(model_input)); 
            } 
        } 
        // ワームアップ 
        for (ov::InferRequest& ireq : ireqs) { 
            ireq.start_async(); 
        } 
        for (ov::InferRequest& ireq : ireqs) { 
            ireq.wait(); 
        } 
        // seconds_to_run 秒と少なくとも niter 反復のベンチマーク 
        std::chrono::seconds seconds_to_run{10}; 
        size_t niter = 10; 
        std::vector<double> latencies; 
        std::mutex mutex; 
        std::condition_variable cv; 
        std::exception_ptr callback_exception; 
        struct TimedIreq { 
            ov::InferRequest& ireq; // ref 
            std::chrono::steady_clock::time_point start; 
            bool has_start_time; 
        }; 
        std::deque<TimedIreq> finished_ireqs; 
        for (ov::InferRequest& ireq : ireqs) { 
            finished_ireqs.push_back({ireq, std::chrono::steady_clock::time_point{}, false}); 
        } 
        auto start = std::chrono::steady_clock::now(); 
        auto time_point_to_finish = start + seconds_to_run; 
        // ireq が完了すると、メインスレッドが起動。 
        // ireq のレイテンシーを計算して保存し、コールバックを設定して次の推論に備える。 
        // コールバックは、インフラストラクチャーが完了すると、その ireq を終了した ireq に再度プッシュします。 
        // 更新されたコールバックで非同期推論を開始 
        for (;;) { 
            std::unique_lock<std::mutex> lock(mutex); 
            while (!callback_exception && finished_ireqs.empty()) { 
                cv.wait(lock); 
            } 
            if (callback_exception) { 
                std::rethrow_exception(callback_exception); 
            } 
            if (!finished_ireqs.empty()) { 
                auto time_point = std::chrono::steady_clock::now(); 
                if (time_point > time_point_to_finish && latencies.size() > niter) { 
                    break; 
                } 
                TimedIreq timedIreq = finished_ireqs.front(); 
                finished_ireqs.pop_front(); 
                lock.unlock(); 
                ov::InferRequest& ireq = timedIreq.ireq; 
                if (timedIreq.has_start_time) { 
                    latencies.push_back(std::chrono::duration_cast<Ms>(time_point - timedIreq.start).count()); 
                } 
                ireq.set_callback( 
                    [&ireq, time_point, &mutex, &finished_ireqs, &callback_exception, &cv](std::exception_ptr ex) { 
                        // コールバックを小さく保ちます。これにより、高速 (数万 FPS) モデルのパフォーマンスが向上します。 
                        std::unique_lock<std::mutex> lock(mutex); 
                        { 
                            try { 
                                if (ex) { 
                                    std::rethrow_exception(ex); 
                                } 
                                finished_ireqs.push_back({ireq, time_point, true}); 
                            } catch (const std::exception&) { 
                                if (!callback_exception) { 
                                    callback_exception = std::current_exception(); 
                                } 
                            } 
                        } 
                        cv.notify_one(); 
                }); 
                ireq.start_async(); 
            } 
        } 
        auto end = std::chrono::steady_clock::now(); 
        double duration = std::chrono::duration_cast<Ms>(end - start).count(); 
        // 結果をレポート 
        slog::info << "Count: " << latencies.size() << " iterations" << slog::endl << "Duration: " << duration << " ms" << slog::endl << "Latency:" << slog::endl; 
        size_t percent = 50; 
        LatencyMetrics{latencies, "", percent}.write_to_slog(); 
        slog::info << "Throughput: " << double_to_string(1000 * latencies.size() / duration) << " FPS" << slog::endl; 
    } catch (const std::exception& ex) { 
        slog::err << ex.what() << slog::endl; 
        return EXIT_FAILURE; 
    } 
    return EXIT_SUCCESS; 
}

各サンプルの明示的な説明は、“OpenVINO™ ランタイムとアプリケーションの統合” ガイドの統合ステップセクションで確認できます。

実行する#

python throughput_benchmark.py <path_to_model> <device_name>(default: CPU)
throughput_benchmark <path_to_model> <device_name>(default: CPU)

サンプルを実行するにはモデルを指定する必要があります。TensorFlow Zoo、HuggingFace、TensorFlow Hub などのモデル・リポジトリーから推論タスクに固有のモデルを取得できます。

#

  1. 事前トレーニングされたモデルをダウンロードします。

  2. 以下を使用して変換できます:

    import openvino as ov 
    
    ov_model = ov.convert_model('./models/googlenet-v1') 
    # または、モデルが Python モデル・オブジェクトの場合 
    ov_model = ov.convert_model(googlenet-v1)
    ovc ./models/googlenet-v1
  3. CPUgooglenet-v1 モデルを使用してベンチマークを実行します。

    python throughput_benchmark.py ./models/googlenet-v1.xml
    throughput_benchmark ./models/googlenet-v1.xml

サンプルの出力#

アプリケーションはパフォーマンス結果を出力します。

[ INFO ] OpenVINO: [ INFO ] Build .................................<version> 
[ INFO ] Count: 2817 iterations 
[ INFO ] Duration: 10012.65 ms 
[ INFO ] Latency: 
[ INFO ]     Median: 13.80 ms 
[ INFO ]     Average: 14.10 ms 
[ INFO ]     Min: 8.35 ms 
[ INFO ]     Max: 28.38 ms 
[ INFO ] Throughput: 281.34 FPS

アプリケーションはパフォーマンス結果を出力します。

[ INFO ] OpenVINO: [ INFO ] Build .................................<version> 
[ INFO ] Count: 1577 iterations 
[ INFO ] Duration: 15024.2 ms 
[ INFO ] Latency: 
[ INFO ]     Median: 38.02 ms 
[ INFO ]     Average: 38.08 ms 
[ INFO ]     Min: 25.23 ms 
[ INFO ]     Max: 49.16 ms 
[ INFO ] Throughput: 104.96 FPS

関連情報#