在多个设备上同时运行

多设备插件简介 (C++)

C++

多设备插件会自动将推理请求分配给可用的计算设备,以并行执行请求。相比之下,异构插件可以在不同设备上运行不同的层,但不能并行。多设备插件拥有下列潜在优势:

  • 通过使用多个设备提高吞吐量(与单设备执行相比)

  • 获得更加一致的性能,因为多个设备可分担推理负载(如果某个设备太忙,则其他设备可以承担更多负载)

请注意,使用多设备时,应用逻辑并未发生变化,因此您无需在每个设备上显式编译模型、创建和平衡推理请求等。从应用的角度而言,这只是另一个处理实际机器的设备。要利用性能,仅需为多设备(以及底层设备)提供足够的推理请求以供处理。例如,如果您在 CPU 上处理 4 个摄像头(有 4 个推理请求),则可能需要处理更多摄像头(执行更多请求)以通过多设备保持 CPU 和 GPU 繁忙。

设置多设备可分为三个主要步骤:

  1. 为每个设备准备配置。

  2. 在基于(配置在第一步准备)已配置设备(优先级)列表创建的多设备插件上编译模型。

  3. 与任何其他 CompiledModel 调用(产生于 compile_model)一样,可根据需要创建尽可能多的请求以使设备饱和。

下面将详细介绍这些步骤。

定义和配置多设备插件

按照 OpenVINO™ 设备命名惯例,多设备插件命名为“MULTI”。多设备插件仅需配置待使用设备的优先级列表:

参数名称

参数值

默认值

描述

ov::device::priorities

以逗号分隔的设备名称,不带空格

不可用

设备优先级列表

您可以将优先级直接设置为字符串。

大体而言,有三种方法可以指定“MULTI”使用的设备:

ov::Core core;
std::shared_ptr<ov::Model> model = core.read_model("sample.xml");
// the "MULTI" device is (globally) pre-configured with the explicit option
core.set_property("MULTI", ov::device::priorities("HDDL,GPU"));
ov::CompiledModel compileModel0 = core.compile_model(model, "MULTI");

// configuration of the "MULTI" is part of the compile configuration (and hence specific to the model):
ov::CompiledModel compileModel1 = core.compile_model(model, "MULTI", ov::device::priorities("HDDL,GPU"));

// same as previous, but configuration of the "MULTI" is part
// of the name (so config is empty), also model-specific:
ov::CompiledModel compileModel2 = core.compile_model(model, "MULTI:HDDL,GPU");

请注意,编译后的模型可实时更改设备优先级:

ov::Core core;
std::shared_ptr<ov::Model> model = core.read_model("sample.xml");
ov::CompiledModel compileModel = core.compile_model(model, "MULTI:HDDL,GPU");
// reverse the order of priorities
compileModel.set_property(ov::device::priorities("GPU,HDDL"));
// you can even exclude some device (HDDL here)
compileModel.set_property(ov::device::priorities("GPU"));
// and then return it back
compileModel.set_property(ov::device::priorities("GPU,HDDL"));
// but you cannot add new devices on the fly, 
// the next line will trigger the following exception:
// [ ERROR ] [NOT_FOUND] You can only change device
// priorities but not add new devices with the model's
// ov::device::priorities. CPU device was not in the original device list!
compileModel.set_property(ov::device::priorities("CPU,GPU,HDDL"));

最后,还有一种方法可以指定多设备在内部为每个设备保留的请求数。假设您的原始应用正在运行 4 个摄像头和 4 个推理请求。您可能希望在 MULTI 中使用的 2 个设备间共享这 4 个请求。最简单的方法是使用括号为每个设备指定请求数:“MULTI:CPU(2),GPU(2)”,并在您的应用中使用相同的 4 个请求。但是,这种显式配置并不具有性能可移植性,因此不推荐。相反,更好的方法是配置单个设备,并查询要在应用级别使用的所得请求数(请参阅配置单个设备和在顶部创建多设备)。

枚举可用设备

OpenVINO 运行时 API 具有枚举设备及其功能的专用方法。请参阅 Hello 查询设备 C++ 样本。这是样本的示例输出(仅截断为设备名称):

./hello_query_device
Available devices:
    Device: CPU
...
    Device: GPU.0
...
    Device: GPU.1
...
    Device: HDDL

枚举设备并与多设备配合使用的简单编程方式如下:

ov::Core core;
std::shared_ptr<ov::Model> model = core.read_model("sample.xml");
std::vector<std::string> availableDevices = core.get_available_devices();
std::string all_devices;
for (auto && device : availableDevices) {
    all_devices += device;
    all_devices += ((device == availableDevices[availableDevices.size()-1]) ? "" : ",");
}
ov::CompiledModel compileModel = core.compile_model(model, "MULTI",
    ov::device::priorities(all_devices));

除了简单的“CPU”、“GPU”、“HDDL”等之外,当一个设备的多个实例可用时,名称会更符合条件。例如,在 hello_query_sample 中这样枚举两个英特尔® Movidius™ Myriad™ X 电脑棒。

...
    Device: MYRIAD.1.2-ma2480
...
    Device: MYRIAD.1.4-ma2480

因此,使用两者的显式配置应为“MULTI:MYRIAD.1.2-ma2480,MYRIAD.1.4-ma2480”。相应地,仅在所有“MYRIAD”类型的可用设备上循环的代码如下:

ov::Core core;
std::vector<std::string> myriadDevices = core.get_property("MYRIAD", ov::available_devices);
std::string all_devices;
for (size_t i = 0; i < myriadDevices.size(); ++i) {
    all_devices += std::string("MYRIAD.")
                            + myriadDevices[i]
                            + std::string(i < (myriadDevices.size() -1) ? "," : "");
}
ov::CompiledModel compileModel = core.compile_model("sample.xml", "MULTI",
    ov::device::priorities(all_devices));

配置单个设备并在顶部创建多设备

如第一节所述,您应该像往常一样配置每个单独的设备,然后只需在顶部创建“MULTI”设备:

// configure the HDDL device first
ov::Core core;
std::shared_ptr<ov::Model> model = core.read_model("sample.xml");
// compile the modle on the multi-device,
// while specifying the configuration (devices along with priorities
// and the configuration of devices):
ov::CompiledModel compileModel = core.compile_model(model, "MULTI",
    ov::device::priorities("HDDL", "GPU"),
    ov::device::properties("HDDL", hddl_config),
    ov::device::properties("GPU", gpu_config));

// query the optimal number of requests:
uint32_t nireq = compileModel.get_property(ov::optimal_number_of_infer_requests);

另一种方法是将所有单独的设备设置合并到单个配置文件中并加载,从而允许多设备插件解析并将设置应用于正确的设备。请参阅下一节中的代码示例。

请注意,虽然加速器的性能在多设备中运行良好,但 CPU+GPU 执行提出了一些性能警告,因为这些设备共享功率、带宽和其他资源。例如,建议启用 GPU 过热降频提示(为 CPU 推理节省另一个 CPU 线程)。请参阅下方多设备和 OpenVINO 样本配合使用以及性能基准测试一节。

查询最佳推理请求数

您可以使用配置设备查询最佳请求数。同样,使用多设备时,无需对包含的设备自行求和,可以直接查询属性:

ov::Core core;
ov::CompiledModel compileModel = core.compile_model("sample.xml", "MULTI:HDDL,GPU");
// query the optimal number of requests
uint32_t nireq = compileModel.get_property(ov::optimal_number_of_infer_requests);

多设备和 OpenVINO 样本配合使用以及性能基准测试

每个支持 -d(代表“设备”)命令行选项的 OpenVINO 样本都公开接受多设备。基准测试应用是优化使用多设备的最佳参考。如前所述,您无需设置请求、CPU 流或线程的数量,因为应用提供了开箱即用的最佳性能。下方示例命令可用于评估 HDDL+GPU 性能:

./benchmark_app d MULTI:HDDL,GPU m <model> -i <input> -niter 1000

多设备插件支持 FP16 IR 文件。CPU 插件自动将其上转换为 FP32,其他设备本身也支持。请注意,尚没有演示针对多设备进行完全优化,包括支持 ov::optimal_number_of_infer_requests 属性、使用 GPU 流/过热降频等。

另请参阅

支持的设备

多设备执行的性能注意事项

本节介绍了多设备执行的几条建议(适用于 Python 和 C++):

  • 当在设备列表中首先指定最快的设备时,多设备通常表现最佳。当请求级别的并行度不够时(例如,执行中的请求数量不足以使所有设备饱和),这一点尤其重要。

  • 与任何面向吞吐量的执行一样,强烈建议直接从 ov:compiled_model 的实例中查询最佳推理请求数。有关更多详细内容,请参阅 benchmark_app 的代码。C++Python 中均存在。

  • 请注意,例如 CPU+GPU 执行配合某些旋钮会表现更好,您可以在相同的基准测试应用样本代码中找到这些旋钮。一个具体示例是禁用 GPU 驱动程序轮询,这反过来需要多个 GPU 流来分摊从设备到主机的较慢推理完成通信。

  • 多设备逻辑总是尝试保存设备无关的、面向用户的推理请求与实际在幕后调度、特定于设备的“工作程序”请求共享的(例如,输入)数据副本。为了便于保存副本,建议按照请求的创建顺序运行请求。

多设备插件简介 (Python)

Python

多设备插件会自动将推理请求分配给可用的计算设备,以并行执行请求。相比之下,异构插件可以在不同设备上运行不同的层,但不能并行。多设备插件拥有下列潜在优势:

  • 通过使用多个设备提高吞吐量(与单设备执行相比)

  • 获得更加一致的性能,因为多个设备可分担推理负载(如果某个设备太忙,则其他设备可以承担更多负载)

请注意,使用多设备时,应用逻辑并未发生变化,因此您无需在每个设备上显式编译模型、创建和平衡推理请求等。从应用的角度而言,这只是另一个处理实际机器的设备。要利用性能,仅需为多设备(以及底层设备)提供足够的推理请求以供处理。例如,如果您在 CPU 上处理 4 个摄像头(有 4 个推理请求),则可能需要处理更多摄像头(执行更多请求)以通过多设备保持 CPU 和 GPU 繁忙。

设置多设备可分为三个主要步骤:

  1. 配置每个设备(使用常规配置设备方法

  2. 在基于已配置设备(优先级)列表创建的多设备插件上编译模型。这是应用中唯一需要的更改。

  3. 与任何其他 CompiledModel 调用(产生于 compile_model)一样,可根据需要创建尽可能多的请求以使设备饱和。

下面将详细介绍这些步骤。

定义和配置多设备插件

按照 OpenVINO™ 设备命名惯例,多设备插件命名为“MULTI”。多设备插件仅需配置待使用设备的优先级列表:

参数名称

参数值

默认值

描述

“MULTI_DEVICE_PRIORITIES”

以逗号分隔的设备名称,不带空格

不可用

设备优先级列表

您可以将配置直接设置为字符串,或者使用 multi/multi_device_config.hpp 文件中的 MULTI_DEVICE_PRIORITIES 指标键,它定义了相同的字符串。

为 MULTI 插件指定设备目标的三种方法

  • 选项 1:在 ie.load_network() 中将优先级列表作为参数传递

    core = Core()

    # Read a network in IR or ONNX format
    model = core.read_model(model_path)
    compiled_model = core.compile_model(model=model, device_name="MULTI:CPU,GPU")
  • 选项 2:将列表作为参数传递,并在执行过程中动态更改优先级。请注意,编译后的模型可实时更改设备优先级:

    core = Core()

    # Read a network in IR or ONNX format
    model = core.read_model(model_path)
    core.set_property(device_name="MULTI", properties={"MULTI_DEVICE_PRIORITIES":"HDDL,GPU"})
    # Change priorities
    core.set_property(device_name="MULTI", properties={"MULTI_DEVICE_PRIORITIES":"GPU,HDDL"})
    core.set_property(device_name="MULTI", properties={"MULTI_DEVICE_PRIORITIES":"GPU"})
    core.set_property(device_name="MULTI", properties={"MULTI_DEVICE_PRIORITIES":"HDDL,GPU"})
    core.set_property(device_name="MULTI", properties={"MULTI_DEVICE_PRIORITIES":"CPU,HDDL,GPU"})
  • 选项 3:使用显式提示来控制设备执行的请求数。有一种方法可以指定多设备在内部为每个设备保留的请求数。如果原始应用运行 4 个摄像头和 4 个推理请求,最好在 MULTI 中使用的 2 台设备间共享这 4 个请求。最简单的方法是使用括号为每个设备指定请求数:“MULTI:CPU(2),GPU(2)”,并在应用中使用相同的 4 个请求。但是,这种显式配置并不具有性能可移植性,因此不推荐。更好的方法是配置单个设备,并查询在应用级别使用的所得请求数。请参阅配置单个设备和在顶部创建多设备

枚举可用设备

OpenVINO 运行时 API 具有枚举设备及其功能的专用方法。请参阅 Hello 查询设备 Python 样本。这是样本的示例输出(仅截断为设备名称):

./hello_query_device
Available devices:
    Device: CPU
...
    Device: GPU.0
...
    Device: GPU.1
...
    Device: HDDL

枚举设备并与多设备配合使用的简单编程方式如下:

    all_devices = "MULTI:"
    core = Core()
    model = core.read_model(model_path)
    all_devices += ",".join(core.available_devices)
    compiled_model = core.compile_model(model=model, device_name=all_devices)

除了简单的“CPU”、“GPU”、“HDDL”等之外,当一个设备的多个实例可用时,名称会更有限定。例如,在 hello_query_sample 中这样枚举两个英特尔® Movidius™ Myriad™ X 电脑棒。

...
    Device: MYRIAD.1.2-ma2480
...
    Device: MYRIAD.1.4-ma2480

因此,使用两者的显式配置应为“MULTI:MYRIAD.1.2-ma2480,MYRIAD.1.4-ma2480”。相应地,仅在所有“MYRIAD”类型的可用设备上循环的代码如下:

    match_list = []
    all_devices = "MULTI:"
    dev_match_str = "MYRIAD"
    core = Core()
    model = core.read_model(model_path)
    for d in core.available_devices:
        if dev_match_str in d:
            match_list.append(d)
    all_devices += ",".join(match_list)
    compiled_model = core.compile_model(model=model, device_name=all_devices)

配置单个设备并在顶部创建多设备

可以像往常一样配置每个单独的设备,然后在顶部创建“MULTI”设备:

    core = Core()
    cpu_config = {}
    gpu_config = {}
    model = core.read_model(model_path)
    core.set_property(device_name="CPU", properties=cpu_config)
    core.set_property(device_name="GPU", properties=gpu_config)
    compiled_model = core.compile_model(model=model, device_name="MULTI:GPU,CPU")
    # Query the optimal number of requests
    nireq = compiled_model.get_property("OPTIMAL_NUMBER_OF_INFER_REQUESTS")

另一种方法是将所有单独的设备设置合并到单个配置文件中并加载,从而允许多设备插件解析并将设置应用于正确的设备。请参阅下一节中的代码示例。

请注意,虽然加速器的性能在多设备中运行良好,但 CPU+GPU 执行提出了一些性能警告,因为这些设备共享功率、带宽和其他资源。例如,建议启用 GPU 过热降频提示(为 CPU 推理节省另一个 CPU 线程)。请参阅下方“多设备和 OpenVINO 样本配合使用以及性能基准测试”章节。

多设备和 OpenVINO 样本配合使用以及性能基准测试

每个支持 -d(代表“设备”)命令行选项的 OpenVINO 样本都公开接受多设备。基准测试应用是优化使用多设备的最佳参考。如前所述,您无需设置请求、CPU 流或线程的数量,因为应用提供了开箱即用的最佳性能。以下是使用基准测试应用评估 CPU+GPU 性能的示例命令:

benchmark_app d MULTI:CPU,GPU m <model>

多设备插件支持 FP16 IR 文件。CPU 插件自动将其上转换为 FP32,其他设备本身也支持。请注意,(尚)没有演示针对多设备进行完全优化,包括支持 OPTIMAL_NUMBER_OF_INFER_REQUESTS 指标、使用 GPU 流/过热降频等。

另请参阅

支持的设备