异构执行

异构执行能够在多个设备上执行一个模型的推理。其目的是:

  • 利用加速器的力量处理模型最繁重的部分,并在回退设备(如 CPU)上执行不支持的操作。

  • 在一次推理中更有效地利用所有可用硬件。

在异构模式下执行可分成两个独立步骤:

  1. 设置操作的硬件相关性(ov::Core::query_model 由异构设备在内部使用)

  2. 将模型编译到异构设备需要将模型拆分为多个部分,在特定设备上对其进行编译(通过 ov::device::priorities),并在异构模式下执行它们。根据相关性将模型分解为多个子图,其中具有相同相关性的一组相关联的操作将成为专用子图。每个子图都在一个专用设备上进行编译,并生成多个 ov::CompiledModel 对象,这些对象通过自动分配的中间张量相互联系起来。

这两个步骤互不关联,相关性可以通过两种方式中的一种来设置,可以单独使用,也可以组合使用(如下所述):manualautomatic 模式。

定义和配置异构设备

按照 OpenVINO™ 的命名惯例,异构执行插件被赋予 "HETERO". 标签。可以在没有额外参数的情况下对其进行定义,从而使用默认值,或者通过以下设置选项来进一步配置:

参数名称和 C++ 属性

属性值

描述

“MULTI_DEVICE_PRIORITIES”
ov::device::priorities

HETERO: <device names>
逗号分隔,无空格

列出可供选择的设备。
设备顺序即视为优先级,
顺序为由高到低。

分配相关性的手动和自动模式

手动模式

它需要使用带有 "affinity" 键值的 ov::Node::get_rt_info 为模型中的所有操作明确设置相关性。

for (auto && op : model->get_ops()) {
    op->get_rt_info()["affinity"] = "CPU";
}
for op in model.get_ops():
    rt_info = op.get_rt_info()
    rt_info["affinity"] = "CPU"

自动模式

此模式根据专用设备(GPUCPUMYRIAD 等)的支持自动决定将哪个操作分配给哪个设备,而在模型编译期间,查询模型步骤由异构设备隐式调用。

自动模式会导致“贪婪”行为,它会根据您指定的优先级为其分配可在给定设备上执行的所有操作(如 ov::device::priorities("GPU,CPU"))。它不考虑设备的特殊性,例如,如果在该层之前或之后没有其他特殊操作,就无法对某些操作进行推理。如果设备插件不支持由异构设备构建的子图拓扑,您应该手动设置相关性。

auto compiled_model = core.compile_model(model, "HETERO:GPU,CPU");
// or with ov::device::priorities with multiple args
compiled_model = core.compile_model(model, "HETERO", ov::device::priorities("GPU", "CPU"));
// or with ov::device::priorities with a single argument
compiled_model = core.compile_model(model, "HETERO", ov::device::priorities("GPU,CPU"));
compiled_model = core.compile_model(model, device_name="HETERO:GPU,CPU")
# device priorities via configuration property
compiled_model = core.compile_model(model, device_name="HETERO", config={"MULTI_DEVICE_PRIORITIES": "GPU,CPU"})

组合使用手动和自动模式

在某些情况下,您可能需要考虑手动调整自动设置的相关性。这通常用于最大限度减少子图总数量,从而优化内存传输。为此,您需要“修复”自动分配的相关性,如下所示:

// This example demonstrates how to perform default affinity initialization and then
// correct affinity manually for some layers
const std::string device = "HETERO:GPU,CPU";

// query_model result contains mapping of supported operations to devices
auto supported_ops = core.query_model(model, device);

// update default affinities manually for specific operations
supported_ops["operation_name"] = "CPU";

// set affinities to a model
for (auto&& node : model->get_ops()) {
    auto& affinity = supported_ops[node->get_friendly_name()];
    // Store affinity mapping using op runtime information
    node->get_rt_info()["affinity"] = affinity;
}

// load model with manually set affinities
auto compiled_model = core.compile_model(model, device);
# This example demonstrates how to perform default affinity initialization and then
# correct affinity manually for some layers
device = "HETERO:GPU,CPU"

# query_model result contains mapping of supported operations to devices
supported_ops = core.query_model(model, device)

# update default affinities manually for specific operations
supported_ops["operation_name"] = "CPU"

# set affinities to a model
for node in model.get_ops():
    affinity = supported_ops[node.get_friendly_name()]
    node.get_rt_info()["affinity"] = "CPU"

# load model with manually set affinities
compiled_model = core.compile_model(model, device)

有一点很重要,如果模型中的任何操作的 "affinity" 已经初始化,那么自动模式将不再起作用。

备注

ov::Core::query_model 不依赖于用户设置的相关性。相反,它根据设备功能来查询操作支持。

配置回退设备

如果希望异构执行中的不同设备具有不同的设备特有配置选项,您可以使用特殊帮助器属性 ov::device::properties

auto compiled_model = core.compile_model(model, "HETERO",
    // GPU with fallback to CPU
    ov::device::priorities("GPU", "CPU"),
    // profiling is enabled only for GPU
    ov::device::properties("GPU", ov::enable_profiling(true)),
    // FP32 inference precision only for CPU
    ov::device::properties("CPU", ov::hint::inference_precision(ov::element::f32))
);
core.set_property("HETERO", {"MULTI_DEVICE_PRIORITIES": "GPU,CPU"})
core.set_property("GPU", {"PERF_COUNT": "YES"})
core.set_property("CPU", {"INFERENCE_PRECISION_HINT": "f32"})
compiled_model = core.compile_model(model=model, device_name="HETERO")

在上面的示例中,GPU 设备的配置用来启用分析数据并使用默认的执行精度,而 CPU 的配置属性用来在 fp32 中执行推理。

处理难解拓扑

有些拓扑对某些设备上的异构执行并不友好,甚至到了无法执行的地步。例如,具有在主设备上不支持的激活操作的模型被异构分解成多组子图,从而导致执行不理想。如果在异构模式中从一个子图向模型的另一部分传输数据,所需的时间比在正常执行的情况下更多,异构执行则可能无法验证。在这类情况下,您可以手动定义最繁重的部分,并设置相关性,以避免在一次推理过程中多次来回发送数据。

分析异构执行性能

在启用 OPENVINO_HETERO_VISUALIZE 环境变量后,您可以转储带有每个设备的操作注释的 GraphViz .dot 文件。

异构执行模式可生成两个文件:

  • hetero_affinity_<model name>.dot- 每项操作的相关性注释。

  • hetero_subgraphs_<model name>.dot- 每个图形的相关性注释。

您可以使用 GraphViz 实用程序或文件转换器来查看图像。在 Ubuntu 操作系统上,您可以使用 xdot:

  • sudo apt-get install xdot

  • xdot hetero_subgraphs.dot

您可以使用性能数据(在示例应用中,它是 -pc 选项)来获取每个子图的性能数据。

下面的输出示例说明的是 Googlenet v1 在 HDDL 运行,可以回退到 CPU:

subgraph1: 1. input preprocessing (mean data/HDDL):EXECUTED layerType:          realTime: 129   cpu: 129  execType:
subgraph1: 2. input transfer to DDR:EXECUTED                layerType:          realTime: 201   cpu: 0    execType:
subgraph1: 3. HDDL execute time:EXECUTED                    layerType:          realTime: 3808  cpu: 0    execType:
subgraph1: 4. output transfer from DDR:EXECUTED             layerType:          realTime: 55    cpu: 0    execType:
subgraph1: 5. HDDL output postprocessing:EXECUTED           layerType:          realTime: 7     cpu: 7    execType:
subgraph1: 6. copy to IE blob:EXECUTED                      layerType:          realTime: 2     cpu: 2    execType:
subgraph2: out_prob:          NOT_RUN                       layerType: Output   realTime: 0     cpu: 0    execType: unknown
subgraph2: prob:              EXECUTED                      layerType: SoftMax  realTime: 10    cpu: 10   execType: ref
Total time: 4212 microseconds

用例

OpenVINO™ 样本程序可以使用与 -d 选项配合使用的异构执行:

./hello_classification <path_to_model>/squeezenet1.1.xml <path_to_pictures>/picture.jpg HETERO:GPU,CPU

在此:

  • HETERO 代表异构执行

  • GPU,CPU 指向回退策略,优先考虑 GPU,可回退到 CPU

您还可以指向两个以上的设备:-d HETERO:MYRIAD,GPU,CPU

另请参阅

支持的设备