0%

简介

  • 事件驱动型架构相关学习笔记
  • 事件驱动型架构是一种以事件为核心进行系统设计和实现的方法。它通过事件的发布和订阅机制,使系统的各个组件之间能够松耦合的进行通信,从而提高系统的灵活性和可维护性。
Read more »

简介

  • UML 相关学习笔记
  • UML,全称为 Unified Model Language,即统一建模语言,是由一整套图标组成的,为面向对象系统的产品进行说明,可视化和编制文档的一种标准语言。UML代表了一组最佳工程实践,这些实践已被证明在大型复杂系统的建模中是成功的。
  • UML是开发面向对象软件和软件开发过程中非常重要的一部分。在嵌入式系统涉及中,使用UML建模并书写文档,通常可以起到事半功倍的效果。
Read more »

windows下C++项目 监控编译速度慢的问题的工具有哪些

在Windows下C++项目中,如果遇到编译速度较慢的问题,可以使用以下工具来监控和分析问题,帮助优化编译速度:


1. 编译时间分析工具

  • Clang Build Analyzer
    Clang 提供的工具,可以帮助分析编译时间瓶颈。虽然它主要针对Clang编译器,但如果项目使用Clang编译,可以直接使用此工具。

    • 官网: Clang Build Analyzer
    • 用途: 分析哪些头文件或代码模块导致了编译速度慢。
  • MSBuild Binary and Structured Log Viewer
    如果你的项目使用MSBuild构建,可以通过MSBuild日志工具查看具体的构建时间段。

    • 下载: MSBuild Log Viewer
    • 用途: 生成并可视化MSBuild日志,快速定位长时间任务。

2. 依赖分析工具

  • Include What You Use (IWYU)
    用于分析C++项目中头文件的依赖情况,减少不必要的头文件包含。

    • 官网: IWYU
    • 用途: 帮助减少头文件依赖,降低编译时间。
  • Visual Studio 编译依赖分析器
    如果使用Visual Studio,可以通过“项目属性”中的编译依赖设置来分析和优化头文件的使用。

    • 用途: 检测包含文件树,分析冗余包含。

3. 构建性能优化工具

  • CMake Timing Tools
    如果使用CMake作为构建系统,可以通过 --trace--trace-expand 参数分析配置时间。还可以开启构建性能统计(例如 Ninja 提供的构建时间统计)。

    • 用途: 分析构建过程中每一步的耗时。
  • IncrediBuild
    专为Windows环境设计的分布式构建工具,可大幅加速编译过程。

    • 官网: IncrediBuild
    • 用途: 通过分布式任务并行化提升编译速度。
  • ccache
    高效的编译缓存工具,用于加速重复编译。

    • 官网: ccache
    • 用途: 缓存已编译文件,避免重复编译。

4. 性能监控工具

  • Windows Performance Analyzer (WPA)
    使用Windows自带的性能分析工具监控磁盘、CPU和内存等资源使用情况,定位瓶颈是否由系统资源导致。

  • Process Monitor (ProcMon)
    监控构建过程中文件系统和进程的读写操作,定位慢点是否由磁盘I/O引起。


5. 其他辅助工具

  • Precompiled Headers (PCH)
    利用预编译头减少重复的头文件解析时间。

  • Ninja
    替代传统的构建工具(如MSBuild或Make),以更快的依赖管理和任务调度提升编译速度。

  • Clang-Tidy
    通过静态分析工具检查代码质量,识别可能导致编译变慢的代码模式。


总结

可以从头文件优化、构建工具、分布式构建和编译缓存等方面入手。推荐先使用IWYUClang Build Analyzer分析依赖,再结合ccacheNinja等工具优化构建流程。

ov::Output 详解

ov::Output 是 OpenVINO 中的一个核心类,用于表示模型图中节点的输出。它封装了一个节点的输出端口,允许用户访问输出的形状、数据类型以及与其他节点的连接信息。通过 ov::Output,可以在计算图中导航并操作节点间的连接。


核心功能

  1. 表示节点的输出端口
    每个 ov::Node 可能有一个或多个输出端口,ov::Output 表示其中一个端口。

  2. 获取元信息
    包括输出的形状、数据类型和元素类型等。

  3. 管理连接关系
    用于查看输出与哪些节点的输入相连接。

  4. 支持模板化
    ov::Output<T> 可以作用于不同类型的节点,例如 ov::Nodeov::Function


创建和使用 ov::Output

通常,ov::Output 是从 ov::Nodeoutputs() 方法或 output(i) 方法获取的,而不是直接创建。

获取节点的输出

1
2
// 获取节点的第一个输出
ov::Output<ov::Node> output = node->output(0);

主要方法和属性

以下是 ov::Output 的常用方法及其功能:

1. 获取输出形状

1
2
3
4
5
6
ov::Shape shape = output.get_shape();
std::cout << "Shape: ";
for (const auto& dim : shape) {
std::cout << dim << " ";
}
std::cout << std::endl;
  • 返回输出的静态形状(ov::Shape)。

2. 获取数据类型

1
2
ov::element::Type type = output.get_element_type();
std::cout << "Data type: " << type << std::endl;
  • 返回输出的数据类型(例如 f32i32 等)。

3. 检查输出是否为动态形状

1
2
3
if (output.get_partial_shape().is_dynamic()) {
std::cout << "Output has a dynamic shape." << std::endl;
}
  • 使用 get_partial_shape() 检查是否为动态形状。

4. 获取连接的输入

1
2
3
4
for (const auto& target_input : output.get_target_inputs()) {
std::cout << "Connected to input of node: "
<< target_input.get_node()->get_friendly_name() << std::endl;
}
  • get_target_inputs() 返回所有连接到该输出的输入端口。

5. 获取输出所属的节点

1
2
3
auto owner_node = output.get_node();
std::cout << "Output belongs to node: "
<< owner_node->get_friendly_name() << std::endl;
  • get_node() 返回该输出所属的节点。

6. 设置输出名称

1
output.set_names({"custom_output_name"});
  • 设置输出的自定义名称,便于识别和访问。

动态修改模型图

通过 ov::Output,可以修改计算图中节点的连接关系。

1. 替换输出连接

1
2
3
4
ov::Output<ov::Node> new_output = some_other_node->output(0);

// 替换当前输出与新输出的连接
ov::replace_output(output, new_output);

2. 添加新连接

可以将一个输出连接到多个输入节点:

1
output.add_target_input(new_node->input(0));

实际应用示例

以下示例展示了如何遍历模型节点的输出,并动态修改模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <openvino/openvino.hpp>
#include <iostream>

int main() {
ov::Core core;

// 加载模型
auto model = core.read_model("model.xml");

// 遍历模型中的所有节点
for (const auto& node : model->get_ops()) {
for (size_t i = 0; i < node->get_output_size(); ++i) {
ov::Output<ov::Node> output = node->output(i);

// 输出节点信息
std::cout << "Node: " << node->get_friendly_name()
<< ", Output index: " << i
<< ", Shape: " << output.get_shape()
<< ", Type: " << output.get_element_type() << std::endl;

// 修改连接(例如替换输出连接)
if (output.get_element_type() == ov::element::f32) {
auto new_node = std::make_shared<ov::op::v0::Relu>(output);
ov::replace_output(output, new_node->output(0));
std::cout << "Replaced output with a ReLU node." << std::endl;
}
}
}

// 保存修改后的模型
core.compile_model(model, "CPU")->export_model("modified_model.xml");

return 0;
}

高级功能

使用动态形状的输出

对于动态形状的输出,可以进一步设置具体形状:

1
2
3
if (output.get_partial_shape().is_dynamic()) {
output.set_partial_shape({1, 3, 224, 224});
}

获取扩展的形状信息

获取动态形状的约束:

1
2
3
4
5
6
ov::PartialShape partial_shape = output.get_partial_shape();
if (partial_shape.rank().is_dynamic()) {
std::cout << "Rank is dynamic." << std::endl;
} else {
std::cout << "Rank: " << partial_shape.rank().get_length() << std::endl;
}

性能注意事项

  1. 批量操作
    尽量减少对输出的逐个操作,使用批量方法操作多个节点或输出。

  2. 减少拷贝
    使用引用或共享机制避免不必要的输出数据拷贝。

  3. 动态形状处理
    在动态模型中,尽量在推理开始前明确所有动态形状。


常见问题

  1. 动态形状的操作限制
    对动态形状的输出操作时,需要先设置具体形状,否则部分操作可能失败。

  2. 数据类型不匹配
    在连接节点时,确保输出和输入的数据类型一致。

  3. 输出未连接
    如果某些输出未连接,可能导致模型推理失败或结果异常。


总结

ov::Output 是 OpenVINO 图操作中的关键类,用于表示和管理节点的输出端口。通过 ov::Output,开发者可以轻松获取输出的元信息,管理节点间的连接关系,并动态修改计算图。熟练掌握 ov::Output 的功能,可以大大提升在 OpenVINO 图操作中的开发效率。