NVIDIA Holoscan SDK v2.9.0

Holoscan SDK 常见问题解答

Q1:什么是 Holoscan SDK?

A1:Holoscan SDK 是 NVIDIA 提供的综合性软件开发工具包,专为开发实时 AI 应用而设计,尤其是在医疗保健领域。它包括加速库、预训练 AI 模型以及用于各种医学成像模式(如超声、内窥镜、外科机器人等)的参考应用程序。

Q2:Holoscan SDK 的核心组件有哪些?

A2:核心组件包括:

  • 应用程序:Fragment 的集合,用于采集和处理流数据。单 Fragment 应用程序在单个进程中执行,而多 Fragment(分布式)应用程序可以跨多个进程和/或物理节点。

  • Fragment:Operator 的有向图,可以分配到 Holoscan 集群中的物理节点。

  • Operator:处理流数据的基本工作单元。

  • 条件:确定给定 Operator 何时被认为准备好执行的组件。

  • 资源:提供可在 Operator 之间重用的共享功能的组件。示例包括设备内存池、CUDA 流池以及用于数据序列化/反序列化的组件。

  • 端口:Operator 的输入端口用于接收来自上游 Operator 的数据。输入端口由接收器和任何相关的条件组成。Operator 的输出端口用于向下游 Operator 发送数据。输出端口由发送器和任何相关的条件组成。

Q3:Holoscan SDK 与其他 SDK 有何不同?

A3:Holoscan SDK 是一款领域和传感器不可知的 SDK,针对高性能、高带宽和实时 AI 应用的简易构建和部署进行了优化。通过将高速仪器与 NVIDIA 软件相结合,Holoscan 成为未来自动驾驶、软件定义和可扩展传感器处理解决方案的平台,触及从科学计算和仪器仪表到医疗设备等行业。

Q1:如何安装 Holoscan SDK?

A1:有多种安装 Holoscan SDK 的方法:

  • 使用 NGC 容器

    • 适用于 dGPU (x86_64, IGX Orin dGPU, Clara AGX dGPU, GH200)

复制
已复制!
            

docker pull nvcr.io/nvidia/clara-holoscan/holoscan:v2.9.0-dgpu

  • 适用于 iGPU (Jetson, IGX Orin iGPU, Clara AGX iGPU)

复制
已复制!
            

docker pull nvcr.io/nvidia/clara-holoscan/holoscan:v2.9.0-igpu

有关更多信息,请参阅 NGC 上的详细信息和使用说明。

  • 使用 Debian 软件包

复制
已复制!
            

sudo apt update sudo apt install holoscan

如果找不到 holoscan,请在重复上述步骤之前尝试以下操作:

请注意,要利用 debian 软件包中包含的 python 模块(而不是安装 python wheel),请将以下路径添加到您的 python 路径中。

复制
已复制!
            

export PYTHONPATH="/opt/nvidia/holoscan/python/lib"

  • 使用 Python wheel

复制
已复制!
            

pip install holoscan

有关更多详细信息和故障排除,请参阅 PyPI。对于 x86_64,请确保 CUDA 工具包已安装

如果您不确定使用哪种安装选项,请参考以下注意事项:

  • NGC 上的 Holoscan 容器镜像 是确保所有依赖项都以预期版本存在(包括 Torch 和 ONNX Runtime)的最安全方法,并且应在大多数 Linux 发行版上工作。它是运行嵌入式示例的最简单方法,同时仍然允许您在其之上创建自己的 C++ 和 Python Holoscan 应用程序。这些优势是有代价的:

    • 镜像尺寸较大,因为依赖项众多(其中一些是可选的)。如果您需要精简的运行时镜像,请参阅以下部分

    • 使用 Docker 时存在的标准不便之处,例如更复杂的运行说明以进行正确配置。

  • 如果您对自己在主机环境中管理依赖项的能力充满信心,那么 Holoscan Debian 软件包 应该提供使用 Holoscan SDK 所需的所有功能,前提是您使用的是 Ubuntu 22.04。

  • 如果您对 C++ API 不感兴趣,而只需要在 Python 中工作,或者想使用 Python 3.10 以外的版本,则可以使用 PyPI 上的 Holoscan Python wheel。虽然它们是安装 SDK 最简单的解决方案,但根据您的需求,它们可能需要最多的工作来设置您的环境和额外的依赖项。最后,它们仅在 Ubuntu 22.04 上正式受支持,尽管它们应该支持其他 glibc 2.35 或更高版本的 Linux 发行版。

Q2:安装 Holoscan SDK 的先决条件是什么?

A2:先决条件包括:

  • 如果您要在开发套件上安装 Holoscan SDK,请参考以下详细信息:

开发套件

用户指南

操作系统

GPU 模式

NVIDIA IGX Orin 用户指南链接 IGX 软件 1.1 Production Release iGPU * dGPU
NVIDIA Jetson AGX Orin 和 Orin Nano 用户指南链接 JetPack 6.0 iGPU
NVIDIA Clara AGX 用户指南链接 Holopack 1.2 iGPU * dGPU
  • 如果您要在 NVIDIA SuperChip 上安装 Holoscan SDK,请注意 HSDK 2.2 仅在 Grace-Hopper SuperChip (GH200) 和 Ubuntu 22.04 上进行了测试。请按照 此处 的设置说明进行操作。

  • 如果您要在 Linux x86_64 工作站上安装 Holsocan SDK,请参考以下详细信息了解支持的发行版:

操作系统

NGC 容器

Debian/RPM 软件包

Python wheel

从源代码构建

Ubuntu 22.04
RHEL 9.x
其他 Linux 发行版

有关特定的 NVIDIA 独立 GPU (dGPU) 要求,请查看下方:

Q3:是否需要任何额外的设置步骤?

A3:为实现最佳性能,可能需要额外的设置步骤,包括:

Q1:如何开始使用 Holoscan SDK 开发应用程序?

A1:入门步骤:

  1. 设置 SDK 和您的开发环境。

  2. 按照 SDK 文档中提供的“入门”指南和教程进行操作。

  3. 浏览 Holohub 中的 示例应用程序,了解框架及其功能。

Q2:SDK 中是否有任何预训练模型?

A2:是的,SDK 包含用于各种医学成像任务(如分割、分类和对象检测)的预训练 AI 模型。这些模型可以进行微调或直接在您的应用程序中使用。有关更多详细信息,请参阅 Holohub 中的内窥镜工具跟踪 示例和人体姿势估计 示例

Q1:如何使用 Holoscan SDK 创建新应用程序?

A1:要创建新应用程序:

  1. 定义核心概念,如 Operator、Fragment 及其交互。

  2. 使用提供的模板和示例作为起点。

有关更多详细信息,请参阅 Holoscan 示例部分

Q2:SDK 中提供了一些什么样的示例应用程序?

A2:示例应用程序包括:

  • Hello World:SDK 的基本介绍。

  • Ping Simple 和 Ping Custom Op:演示简单的数据处理。

  • 视频重放器:展示如何处理和显示视频流。

  • 医学成像示例,如超声和内窥镜处理

有关示例应用程序列表,请访问 Holoscan 示例部分

Q3:如何将我自己的 AI 模型集成到 Holoscan SDK 中?

A3:集成您自己的 AI 模型涉及:

  1. 将您的模型转换为兼容的格式(例如,TensorRT、ONNX)。

  2. 确保您的数据预处理和后处理步骤与模型的要求一致。

有关如何在 Holsocan SDK 中引入您自己的模型并构建推理示例的更多信息,请参阅 示例。

Q4:如何更新 VideoStreamReplayerOp 和 VideoStreamRecorderOp 以使用自定义文件格式?

A4:Holoscan SDK 依赖于 GXF。GXF 使用 实体组件系统 范例。Holoscan 使用 GXF 作为执行引擎,Holoscan 的 API 抽象了实体组件系统,并将 GXF 节点抽象为带有输入/输出端口的 Operator。

image1.png

Codelet(Holoscan 中的 Operator)之间的大多数消息也是实体对象。一个实体可以包含多个组件/类型。在 VideoStreamReplayerOp 和 VideoStreamRecorderOp 中,发送/接收的是包含一个或多个 GXF Tensor 对象(作为组件)的实体对象 - 您可以将实体视为对象字典 – <key,object> 映射。对于 VideoStreamReplayerOp 和 VideoStreamRecorderOp,它目前使用自定义文件类型( .gxf_entities.gxf_index 文件)来加载和存储实体对象序列(在本例中,实体有一个 GXF Tensor 对象)。 .gxf_index 文件包含每个实体的文件偏移量/时间戳信息,而 .gxf_entities 包含一系列(序列化的)实体数据。实体对象的序列化/反序列化是通过使用 nvidia::gxf::EntitySerializer 类(使用 StdEntitySerializer 实现)和 nvidia::gxf::FileStream 端点完成的。在 GXF 中支持 GDS 的官方方法是扩展 nvidia::gxf::FileStream 类,使其在内部使用 cufile(GDS)。但是,设置开发环境并不简单。以下是您需要遵循的步骤:

  • 更新 VideoStreamRecorderOp::compute() 以使用您自己的实现将实体(作为单个张量)保存到文件系统。

    • 示例

复制
已复制!
            

auto in_message = op_input.receive<holoscan::TensorMap>("in").value();

  • 更新 VideoStreamReplayerOp::compute() 以使用您自己的实现读取文件(使用自定义格式)并将其作为实体(包含张量作为组件 – 称为 TensorMap)发出。

    • 示例

复制
已复制!
            

nvidia::gxf::Expected<nvidia::gxf::Entity> out_message = CreateTensorMap(context.context(), pool.value(), {{out_tensor_name_.get(), nvidia::gxf::MemoryStorageType::kDevice, out_shape, out_primitive_type_, 0, nvidia::gxf::ComputeTrivialStrides(out_shape, dst_typesize)}}, false);

  • 您需要更新 initialize() 和其他方法以删除 nvidia::gxf::FileStream 和 nvidia::gxf::FileStream 端点。为了测试 VideoStreamReplayerOp,您可以直接使用 VideoReplayerApp 示例。您可以通过遵循用户指南来开发/测试/创建 Operator(发布)二进制文件: https://github.com/nvidia-holoscan/holoscan-sdk/blob/main/DEVELOP.md

复制
已复制!
            

class VideoReplayerApp : public holoscan::Application { public: void compose() override { using namespace holoscan; // Sets the data directory to use from the environment variable if it is set ArgList args; auto data_directory = std::getenv("HOLOSCAN_INPUT_PATH"); if (data_directory != nullptr && data_directory[0] != '\0') { auto video_directory = std::filesystem::path(data_directory); video_directory /= "racerx"; args.add(Arg("directory", video_directory.string())); } // Define the replayer and holoviz operators and configure using yaml configuration auto replayer = make_operator<ops::VideoStreamReplayerOp>("replayer", from_config("replayer"), args); auto visualizer = make_operator<ops::HolovizOp>("holoviz", from_config("holoviz")); // Define the workflow: replayer -> holoviz add_flow(replayer, visualizer, {{"output", "receivers"}}); } };

复制
已复制!
            

./run build ./run launch # inside the container ./examples/video_replayer/cpp/video_replayer

  • 作为替代方案,您可以创建一个单独的 Holoscan Operator,并将其与其他示例应用程序(如内窥镜工具跟踪应用程序)一起应用,方法是遵循 HoloHub 的指南 (https://github.com/nvidia-holoscan/holohub)。您还可以将 Holoscan SDK 的安装二进制文件与通过使用 Holoscan SDK 仓库的 ./run build 创建的 holoscan 安装目录 一起使用。

Q5:如何在 Python 张量中使用推理 Operator?

A5:推理 Operator 接受 holoscan::Entity 或 holoscan::TensorMap(类似于 Python 中 Array-like 对象的字典)作为输入消息。

例如,您可以定义一个处理输入视频(作为张量)的 Operator。您可以通过参考张量互操作 示例 来找到此类 Operator 的更详细示例以及一个示例。

复制
已复制!
            

This operator has: inputs: "input_tensor" outputs: "output_tensor" The data from each input is processed by a CuPy gaussian filter and the result is sent to the output. ``` def compute(self, op_input, op_output, context): # in_message is of dict in_message = op_input.receive("input_tensor") # smooth along first two axes, but not the color channels sigma = (self.sigma, self.sigma, 0) # out_message is of dict out_message = dict() for key, value in in_message.items(): print(f"message received (count: {self.count})") self.count += 1 cp_array = cp.asarray(value) # process cp_array cp_array = ndi.gaussian_filter(cp_array, sigma) out_message[key] = cp_array op_output.emit(out_message, "output_tensor") ```

Q6:Holoscan SDK 是否支持 Triton Python 后端编写的模型,例如 NVIDIA 的 FoundationPose?

A6:目前不支持 Triton 后端。推理 Operator 支持 TensorRT (trt)、ONNX Runtime (onnxrt) 和 Torch 后端。

有关推理 Operator 的更多信息,请参阅用户指南中关于推理 Operator 的 部分

Q7:我可以将 .pth (PyTorch) 模型文件直接与 Holoscan SDK 的推理 Operator 一起使用吗? A7:不可以,您不能将 .pth 模型文件直接与 Holoscan SDK 一起使用。原因如下,以及您可以采取的替代方案:

  1. Holoscan SDK 的 Torch 后端基于 libtorch,这需要模型采用 TorchScript 格式。

  2. 将 .pth 模型转换为 TorchScript 是一个手动过程,无法在 SDK 中自动完成。

  3. 为了获得最佳性能和易用性,建议:a) 如果可用,请使用 TensorRT (TRT) 模型。 b) 如果您有 ONNX 模型,则可以在 SDK 中自动将其转换为 TRT。

  4. 使用 TRT 模型(或从 ONNX 转换为 TRT)可能会提供最快的推理速度,并且最容易使用 Holoscan SDK 进行设置。

总而言之,虽然不支持直接使用 .pth 文件,但转换为 TensorRT 或使用 ONNX 并进行自动 TRT 转换是获得最佳性能和与 Holoscan SDK 兼容性的推荐方法。

Q8:我可以在推理 Operator 中使用多个模型吗?

A8:可以,您可以通过在 model_path_map 参数中指定它们来使用多个模型。有关更多信息,请参阅 Holoscan 用户指南中推理 Operator 的 参数部分

Q9:如何为多个模型启用并行推理?

A9:默认情况下启用并行推理。要禁用它,请在参数集中设置 parallel_inference: false。有关更多信息,请参阅 Holoscan 用户指南中推理 Operator 的 参数部分

Q9:我可以在同一应用程序中为不同的模型使用不同的后端吗?

A9:可以,您可以使用 backend_map 参数为不同的模型指定不同的后端。有关更多信息,请参阅 Holoscan 用户指南中推理 Operator 的 参数部分

Q10:我可以在 CPU 上执行推理吗?

A10:可以,您可以通过设置 infer_on_cpu: true 并使用 ONNX Runtime 或 PyTorch 后端在 CPU 上执行推理。有关更多信息,请参阅 Holoscan 用户指南中推理 Operator 的 参数部分

Q11:我可以控制输入和输出数据的存储位置(CPU 内存与 GPU 内存)吗?

A11:可以,使用 input_on_cuda, output_on_cudatransmit_on_cuda 参数来控制数据位置。有关更多信息,请参阅 Holoscan 用户指南中推理 Operator 的 参数部分

Q12:如何使用 Optional 标志?

A12:在 Python 中,有两种定义参数的方法:

  • 在 Operator 的 Python setup() 方法中使用 spec.param() 方法,通常在包装现有 C++ Operator 时完成。

  • 参数直接传递给构造函数 (__init__())。在 Python 中,参数中没有 try_get() 方法。相反,默认值设置为 None,这允许我们通过验证参数值是否为 None 来检查用户是否设置了参数。

Q13:如何定义 Operator 的创建器以传递自定义参数?

A13:将自定义数据馈送到 compose() 方法中 Operator 的构造函数至关重要。当您在 C++ 中使用 make_operator<>() 模板方法或 Python Operator 构造函数时,会在内部调用 setup() 方法,这会阻止您在调用 make_operator<>() 后传递自定义数据(例如配置值)。在 C++ 中,要将非条件/参数数据传递给 C++ Operator 类的构造函数,您需要定义一个额外的构造函数来接受您的自定义数据。例如,您可以定义一个构造函数,该构造函数接受 std::vector<std::string> 参数作为输出端口名称列表作为第二个参数。

Q14:如何停止应用程序?

A14:有两种停止应用程序的方法:

  • 在重放器 Operator 上使用 BooleanCondition

复制
已复制!
            

std::string op_name = "replayer"; std::string param_name = "boolean_scheduling_term"; // get the operator holoscan::Operator* op = nullptr; auto& app_graph = fragment()->graph(); if (!app_graph.is_empty()) { op = app_graph.find_node(op_name).get(); } if (!op) { HOLOSCAN_LOG_ERROR("Operator '{}' is not defined", op_name); return; } // Stop executing compute() for 'replayer' operator auto boolean_condition = op->condition<holoscan::BooleanCondition>(param_name); boolean_condition->disable_tick(); // Stop executing compute() for this operator boolean_condition = condition<holoscan::BooleanCondition>(param_name); boolean_condition->disable_tick(); return;

为了平稳地终止应用程序,建议依赖调度程序中的 stop-on-deadlock 功能。默认情况下,GreedySchedulerstop_on_deadlock 参数设置为 true。如果 VideoReplayer Operator 停止,则整个管道将停止。

  • 使用 interrupt()

复制
已复制!
            

fragment()->executor().interrupt();

请注意,使用 interrupt() 会强制终止执行,并可能导致错误消息,建议使用基于死锁的方法。

作为替代方案,您还可以使用 CountCondition。请参阅 部分。在高层次上,这就是将 CountCondition 附加到 Operator 的工作方式:

Operator 以 READY 状态启动。每次 Operator 执行时,计数减 1。当计数达到 0 时,Operator 的状态变为 NEVER。在 NEVER 状态下,Operator 停止执行。

例如,如果您想运行应用程序 100 次然后停止它:

复制
已复制!
            

auto my_operator = make_operator<MyOperator>("my_operator", make_condition<CountCondition>(100));

Q15:如何在 Operator 中循环 output.emit() 调用?

A15:每个输入或输出端口都有自己的队列。在内部,该过程的工作方式如下:

  1. 在触发 Operator A 的 compute() 方法之前,对于每个输入端口(通常由 DoubleBufferReceiver 支持),输入端口队列后台的数据(消息)将移动到队列的主台。这是使用 router->syncInbox(entity) 完成的。

  2. 触发 Operator A 的 compute() 方法。

  3. 对于 Operator A 的每个输出端口,输出端口队列中的数据将使用 router->syncOutbox(entity) 移动到下游 Operator 输入端口的队列(后台)。

默认情况下,输入/输出端口的队列容量设置为 1,尽管可以在 setup() 方法中配置此容量。这就是为什么我们不能在 compute() 方法中多次调用 output.emit(),因为这样做可能会导致 GXF_EXCEEDING_PREALLOCATED_SIZE 错误。

使用 GreedyScheduler,它是默认的调度程序,使用单线程来触发 Operator 的 compute() 方法,在当前 Operator 的 compute() 方法返回之前,无法调度其他 Operator。

为了应对这一挑战,我们可能会考虑创建一个实用方法或类,旨在接受生成器或迭代器对象。这种方法在 compute() 方法中尤其有效,尤其是在 Operator 是没有输入端口的源 Operator 的情况下。它将使该方法能够保留输入的状态,并在单个 compute() 调用中为每个产生的值调用 output.emit(),或者在不阻塞线程的情况下返回。

如果我们想要容量为 20 且策略为“reject”(丢弃)的队列(如果队列已满),则 Python API 代码覆盖连接器将类似于以下代码:

复制
已复制!
            

from holoscan.core import IOSpec # and then within the setup method define the output using the connector method like this spec.output("out1").connector( IOSpec.ConnectorType.DOUBLE_BUFFER, capacity=20, policy=1 )

对于策略选项:

  • 0 = pop(如果队列已满,则从队列中删除一个项目,以便为传入的项目腾出空间)

  • 1 = reject(如果队列已满,则拒绝新项目)

  • 2 = fault(如果队列已满并且添加了新项目,则终止应用程序)

为了完整起见,要显式指定连接器及其条件,语法应为:

复制
已复制!
            

# The default setting for an output should be equivalent to explicitly specifying spec.output("out1").connector( IOSpec.ConnectorType.DOUBLE_BUFFER, capacity=1, policy=2 ).condition( ConditionType.DOWNSTREAM_MESSAGE_AFFORDABLE, min_size=1, front_stage_max_size=1 )

Q16:如何向 Holoviz Operator 添加绿色边框和小图像到角落?

A16:您可以按照此处的 Holoviz 示例进行操作:

Q17:setupinitialize__init__ 之间有什么区别?

A17:自 v0.6 版本发布以来,Holoscan Operator 执行“延迟初始化”,Operator 实例创建 (super().__init__(*args, **kwargs) ) 不再初始化(调用 Operator.initialize(self) )相应的 GXF 实体。目前,当 Operator 被 GXF 执行器初始化 时,会完成 在 Python 中设置类成员。setup 方法的目的是通过向方法提供 OperatorSpec 对象(spec 参数)来获取“operator 的规范”。当调用 __init__ 时,它会调用 C++ 的 Operator::spec(const std::shared_ptr<OperatorSpec>& spec) 方法(并设置 self.spec 类成员),并调用 setup 方法,以便 Operator 的 spec() 方法保持 operator 的规范。由于 setup 方法可以使用其他 OperatorSpec 对象多次调用(例如,枚举 operator 的描述),因此在 setup 方法中,用户不应在 Operator 对象中初始化任何内容。此类初始化需要在 initialize 方法中完成。__init__ 方法用于创建 Operator 对象。它可以通过传递各种参数来初始化 operator 对象本身,但它不会“初始化”相应的 GXF 实体对象。

Q18:我想在非 Holoscan 库(OpenCV、CuPy、PyTorch)中使用 Holoscan SDK 分配的 CUDA 流。所有这些第三方库都支持 CUDA 流、分配器等,但它们具有不同的对象来表示该 CUDA 流(例如 cupy.cuda.Stream)。我需要获取 Holoscan CUDA 流并将其转换为 cupy.cuda.Stream,其方式与 Holoscan Tensor 使用内存指针转换为 CuPy 数组的方式类似。请提出一个解决方案。

A18:有一个 CudaStreamHandler 实用程序,它通过 C++ 层中的 GXF API 工作。我们目前尚未创建 Python API,以允许用户从本机 Python operator 的 compute 方法中使用它。一般来说,底层 GXF 库目前正在重构 CUDA 流的处理方式,我们计划在此之后改进 Holoscan SDK 上的流处理。您可以在自己的本机 operator 中使用 CuPy 或其他第三方流 API,并在您自己的本机 operator 之间传递流对象作为 Python 对象。我认为这无助于您面临的问题,因为您想重用由 SDK 提供的某些上游包装的 C++ operator 分配的流,目前没有从 Python 执行此操作的正确方法。

Q19:Holoscan Holoinfer operator 中的 activation_map 参数的用途是什么? A19:activation_map 参数允许用户在运行时动态启用或禁用模型推理。它可以用于决定对每个模型的哪些帧运行推理。

Q20:是否有现有的示例或模板演示如何在 Holoscan 应用程序管道中同时使用集成 GPU (iGPU) 和独立 GPU (dGPU)?具体来说,我正在寻找一个示例工作流程,其中包括:

  1. 在 AGX Orin 的 iGPU 上接收和处理数据

  2. 将处理后的数据传输到 dGPU

  3. 在 dGPU 上运行多个 AI 模型

  4. 使用 dGPU 显示结果

A20:要利用 IGX 系统上带有 Holoscan 的集成 GPU (iGPU) 和独立 GPU (dGPU),请参阅 IGX 用户指南。本指南提供了有关在 IGX 开发套件配置为 dGPU 模式时在容器中使用 iGPU 的详细说明。

对于 Holoscan 应用程序,有两种主要方法可以利用两个 GPU:

  1. 并发应用程序执行:如 IGX 文档中所述,同时运行单独的应用程序。 iGPU 应用程序必须在 Holoscan iGPU 容器中执行,而 dGPU 应用程序可以在本机或 Holoscan dGPU 容器中运行。

  2. 分布式应用程序:开发一个使用两个 GPU 的单个分布式应用程序,方法是在 iGPU 和 dGPU 上分别执行不同的 Fragment。

为了说明第二种方法,请考虑以下使用“ping”分布式应用程序的示例。这演示了使用 Holoscan 容器在 iGPU 和 dGPU 之间的通信:

复制
已复制!
            

COMMON_DOCKER_FLAGS="--rm -i --init --net=host --runtime=nvidia -e NVIDIA_DRIVER_CAPABILITIES=all --cap-add CAP_SYS_PTRACE --ipc=host --ulimit memlock=-1 --ulimit stack=67108864 " HOLOSCAN_VERSION=2.2.0 HOLOSCAN_IMG="nvcr.io/nvidia/clara-holoscan/holoscan:v$HOLOSCAN_VERSION" HOLOSCAN_DGPU_IMG="$HOLOSCAN_IMG-dgpu" HOLOSCAN_IGPU_IMG="$HOLOSCAN_IMG-igpu" # Pull necessary images docker pull $HOLOSCAN_DGPU_IMG docker pull $HOLOSCAN_IGPU_IMG # Execute ping distributed (Python) in dGPU container # Note: This instance serves as the 'driver', but the iGPU could also fulfill this role # The '&' allows for non-blocking execution, enabling subsequent iGPU command docker run \ $COMMON_DOCKER_FLAGS \ $HOLOSCAN_DGPU_IMG \ bash -c "python3 ./examples/ping_distributed/python/ping_distributed.py --gpu --worker --driver" & # Execute ping distributed (C++) in iGPU container docker run \ $COMMON_DOCKER_FLAGS \ -e NVIDIA_VISIBLE_DEVICES=nvidia.com/igpu=0 \ $HOLOSCAN_IMG-igpu \ bash -c "./examples/ping_distributed/cpp/ping_distributed --gpu --worker"

Q21:使用 Holoscan 配置并发处理流水线是否有高效的方法?我的目标是实现一个系统,其中帧采集和处理可以同时进行。具体来说,我旨在当前帧仍在通过 InferenceOp 进行处理时,启动后续帧的读取。为了说明

image2.png

  1. 是否可以在帧 N 仍在 InferenceOp 处理时,开始读取帧 N+1?

  2. 还是 Holoscan 要求在启动帧 N+1 的任何操作之前,完成对帧 N 的所有操作?

如果可以实现并发处理,那么在 Holoscan 框架内实施此类系统的推荐方法是什么?

A21: NVIDIA GXF 框架提供了一个 nvidia::gxf::BroadcastCodelet,其“循环”模式提供了标准广播行为的替代方案。此模式按顺序将输入消息轮流定向到不同的输出端口。虽然此功能在 Holoscan 2.1 中可以通过 GXFCodeletOp 访问,但我们也可以开发一个提供同等效用的原生算子。

GXF 源定义了以下模式

复制
已复制!
            

enum struct BroadcastMode { kBroadcast = 0, // publishes incoming message to all transmitters kRoundRobin = 1, // publishes incoming message to one transmitter in round-robin fashion };

对于收集操作,GXF 实现了 nvidia::gxf::Gather 代码小组件。此代码小组件将来自接收端口的任何消息传输到输出端口。tick_source_limit 参数可以配置为在达到指定的最大输出消息数后,停止检查其他端口上是否有其他接收到的消息。

重要的是要注意,GXF Gather 代码小组件可能不会固有地保留推理操作被调用的顺序。虽然如果推理操作按顺序完成,消息可能会自然地按照接收顺序处理,但此行为不能保证。

为了确保严格的排序,我们可以开发一个自定义的原生算子。此算子将按顺序检查每个端口上的消息,从端口 1 开始,并且仅在当前端口收到消息后才移动到下一个端口。这种方法将保证帧按照正确的顺序处理和显示。

这种自定义实现将提供对消息流的更大控制,并确保处理流水线的完整性,尤其是在保持原始帧顺序至关重要的场景中。

Q22: 如何在我的 Holoscan SDK 应用程序流水线中使用其他库? A22: 请参阅专门的 HoloHub 教程,了解如何在自定义 Holoscan SDK 应用程序中使用外部 C++ 或 Python 库的概述。

Q23: 如何在具有分支路径的 Holoscan 流水线中确保正确的数据流和处理,尤其是在对共享数据执行内联更新时?

A23:在具有分支路径的 Holoscan 流水线中,例如

复制
已复制!
            

A -> B -> C -> D \ -> E

有几个注意事项和潜在的解决方案可以确保正确的数据流和处理,尤其是在像 C 或 D 这样的算子对数据执行内联更新时。

  1. 数据访问保证:E 保证可以访问从 B 出口的数据。但是,在多线程调度器中,必须仔细注意潜在的数据竞争条件。

  2. 执行顺序:当前实现维护根节点的顺序,但是 B -> C 和 B -> E 之间 GXF 连接的创建是随机确定的,因为使用了 std::unordered_map 作为图数据结构。这种随机性可能会影响 GXF 中哪个连接被优先处理。

  3. 潜在的解决方案:a. PeriodicCondition:一种方法是使用 PeriodicCondition 来控制算子的执行时序。这是一个例子

复制
已复制!
            

from holoscan.conditions import CountCondition, PeriodicCondition from holoscan.core import Application from holoscan.operators import PingRxOp, PingTxOp class MyPingApp(Application): def compose(self): b = PingTxOp(self, CountCondition(self, 10), name="B") c = PingRxOp(self, PeriodicCondition(self, 20_000_000), name="C") e = PingRxOp(self, name="E") self.add_flow(b, c) self.add_flow(b, e)

在此示例中,PeriodicCondition 用于确保 C 仅在指定的周期(在本例中为 20 毫秒)过去后执行。这可以帮助控制不同分支之间的数据处理时序。 b. 自定义算子:开发自定义原生算子可以提供对消息流的更多控制。此算子可以按顺序检查每个端口上的消息,确保帧按照正确的顺序处理和显示。 c. 数据复制:为了避免内联更新影响共享数据的问题,请考虑实施一种机制来为每个分支创建数据副本。这确保了一个分支中的修改不会无意中影响另一个分支。

  1. 局限性和注意事项

  • GXF Gather 代码小组件可能不会固有地保留推理操作被调用的顺序。

  • 当使用模拟对象进行测试时(如背景信息中所述),修改原始流水线结构可能具有挑战性。在这种情况下,专注于数据复制或仔细的时序控制可能更可行。

  1. 未来的改进:更新图结构以使用 std::map 而不是 std::unordered_map 作为 succ_pred_ 可能会在连接创建顺序方面提供更可预测的行为。

Q24:我正在询问是否有一个 Holoscan 示例可以演示跨两台联网计算机的分布式处理。具体来说,我对以下场景感兴趣

  1. 一台计算机捕获帧,可能使用 AJA 采集卡。

  2. 然后,捕获的帧通过本地网络传输到第二台计算机。

  3. 第二台计算机接收并显示帧。

此外,我对这种设置的网络方面有一些疑问

  1. Holoscan 在为此计算机间通信选择传输层方面是否提供了灵活性?

  2. 在这种情况下是否可以使用 WebRTC 作为传输协议?

A24: 有两种相关的方法

  1. WebRTC 实现:在 HoloHub 存储库中提供了一个演示基于 WebRTC 的视频流的参考应用程序。您可以在以下位置找到此示例: https://github.com/nvidia-holoscan/holohub/tree/main/applications/webrtc_video_server 此应用程序展示了如何在 Holoscan 框架内将 WebRTC 用于计算机间通信。

  2. 分布式应用程序方法:另一种方法是创建分布式应用程序,每个节点上运行单独的片段。有关更多信息,请参阅用户指南中关于 创建分布式应用程序 的部分。

Q25: 如何使用 run_async() 在单独的线程中启动应用程序并停止应用程序?

A25:我们可以将事件状态设置为 EVENT_WAITING(已发送到异步服务的请求,待处理的事件完成通知),然后设置为 EVENT_DONE(已收到事件完成通知,实体已准备好被滴答),以允许特定的算子等待/恢复其操作。在该示例中,它调用 AsynchronousCondition::event_state(AsynchronousEventState::EVENT_NEVER) 将条件的状态设置为 NEVER。(当调用 BooleanCondition::disable_tick() 时,BooleanCondition 通过将条件的状态设置为 NEVER 来执行相同的操作)。这意味着算子不想再次被滴答(执行结束)。一旦条件的状态变为 NEVER(内部为 SchedulingConditionType::NEVER),它将标记执行结束并且无法撤消。

Q26:在 Holoscan 生态系统中,是否有任何现有的应用程序或示例可以演示在单个应用程序中同时利用多个 GPU 的能力?

A26:多人工智能超声应用程序在不同的 YAML 文件中具有 多 GPU 设置。它可以通过推理参数来控制。

Q27: 调度器在 Holoscan 中的作用是什么?

A27: 调度器负责确定应用程序中每个算子的执行时间。

Q28: Holoscan SDK 中有多少种调度器可用?

A28: 有三种 调度器 可用:贪婪调度器、多线程调度器和基于事件的调度器。

Q29:非分布式应用程序默认使用哪个调度器?

A29:非分布式应用程序默认使用贪婪调度器。

Q30:贪婪调度器的主要特点是什么?

A30: 贪婪调度器只有一个工作线程,该线程以确定性的顺序顺序执行算子。

Q31:多线程调度器如何工作?

A31: 它是一个基于轮询的调度器,具有用户定义数量的工作线程和一个专用线程,该线程以用户定义的间隔轮询算子。

Q32:基于事件的调度器的独特之处是什么?

A32:基于事件的调度器等待指示算子就绪状态发生更改的事件,而不是不断轮询。

Q33:基于事件的调度器如何降低 CPU 开销?

A33:通过消除持续轮询的需要,与某些情况下的多线程调度器相比,它可以显着降低 CPU 使用率。

Q34:在什么情况下,多线程和基于事件的调度器会显示出优势?

A34:在具有可以同时运行的多个算子的场景中,它们会显示出优势,与贪婪调度器相比,可能会提供显着的加速。

Q35:多线程和基于事件的调度器在性能方面如何比较?

A35:它们通常具有相似的运行时性能,但基于事件的调度器往往平均具有较低的 CPU 开销。

Q36:是否存在使用多线程调度器可能没有益处的场景?

A36: 是的,对于线性推理流水线或每个算子计算量最小的应用程序,多线程调度器可能不会提供显着的好处,甚至可能引入开销。

Q37: 工作线程的数量如何影响多线程调度器中的性能?

A37: 增加工作线程的数量可以在一定程度上提高性能,但也会增加 CPU 使用率。

Q38: 是否有任何内存池(分配器)同时支持主机和设备内存?

请为此目的使用 RMMAllocator。它支持用于 CUDA 设备内存和固定主机内存的同步内存池。BlockMemoryPool 可以用于主机或设备内存,但不能同时支持这两种类型。UnboundedAllocator 也支持主机和设备内存,但不是内存池(它每次分配和释放新内存)。

Q1: Holoscan SDK 中有哪些性能工具可用?

A1: Holoscan SDK 提供了多种性能工具,包括数据流跟踪、GXF 作业统计信息和视频流水线延迟工具。

Q2: Holoscan SDK 中的数据流跟踪是什么?

A2: 数据流跟踪是一种分析应用程序并分析片段图中算子之间细粒度的时序属性和数据流的机制。有关更多详细信息,请参阅 Holoscan 用户指南中的 数据流跟踪部分

Q3: 如何在我的 Holoscan 应用程序中启用数据流跟踪?

A3:您可以通过在 C++ 中调用 track() 方法或在 Python 中使用 Tracker 类,然后在运行应用程序之前启用数据流跟踪。

Q4: 我可以使用数据流跟踪检索哪些指标?

A4: 您可以检索诸如最大、平均和最小端到端延迟以及从根算子发送的消息数量等指标。

Q5: 如何自定义数据流跟踪?

A5:您可以通过配置诸如在开始和结束时要跳过的消息数量以及设置延迟阈值以忽略异常值等参数来自定义数据流跟踪。

Q6: 如何启用 GXF 作业统计信息?

A6:您可以通过将环境变量 HOLOSCAN_ENABLE_GXF_JOB_STATISTICS 设置为 true 来启用 GXF 作业统计信息。

Q7: 我可以将 GXF 作业统计信息保存到文件吗?

A7:是的,您可以通过设置 HOLOSCAN_GXF_JOB_STATISTICS_PATH 环境变量将 GXF 作业统计信息保存到 JSON 文件。有关 GXF 作业统计信息的更多信息,请参阅用户指南中的 部分。

Q8: 视频流水线延迟工具如何工作?

A8:它生成一系列已知的视频帧,使用物理环回电缆将它们传输回输入组件,并测量过程中各个阶段的延迟。

Q9: 视频流水线延迟工具可以模拟 GPU 工作负载吗?

A9:是的,该工具可以选择通过在帧生成之前运行任意 CUDA 循环指定的次数来模拟 GPU 工作负载。有关视频流水线延迟工具的更多信息,请参阅用户指南中的 部分。

Q10: 视频流水线延迟工具支持哪些类型的生产者和消费者?

A10:该工具支持各种生产者(OpenGL GPU 直接渲染、GStreamer GPU 渲染、AJA 视频系统)和消费者(V4L2、GStreamer、AJA 视频系统),用于不同的视频输入/输出场景。

Q11:NVTX 标记在 Holoscan SDK 中如何工作?

A11:以下是在 Holoscan 应用程序中添加 NVTX 标记的工作方式

  • 多线程调度器启动一个工作线程,检查实体(也称为 Holoscan 算子)的状态,并使用 GXF 中的 EntityExecutor::executeEntity() 方法执行每个实体。

  • EntityExecutor::executeEntity() 为给定的实体 ID 调用 EntityExecutor::EntityItem::execute() GXF 方法

  • EntityExecutor::EntityItem::execute() 方法检查实体(Holoscan 算子)的调度状态,然后调用 GXF 中的 EntityExecutor::EntityItem::tick() 方法

  • 在 EntityExecutor::EntityItem::tick() 方法中,会发生注释,执行以下步骤

  1. 调用 router->syncInbox(entity); 以同步收件箱。例如,对于给定的 Holoscan 算子,UCXReceiver(输入端口)从网络接收数据并将其推送到 UCXReceiver 对象中的队列中。可以通过在 Operator::compute() 方法中调用 receive() 方法来检索队列中的数据。

  2. 对于实体中的每个代码小组件(在 Holoscan 中,一个实体只能有一个代码小组件),调用 EntityExecutor::EntityItem::tickCodelet(),这反过来又调用 Codelet::tick()(在 Holoscan 中,这是 Operator::compute() 方法)(图 5)。

  3. 调用 router->syncOutbox(entity); 以同步发件箱。例如,对于给定的 Holoscan 算子,通过 Operator::compute() 方法中的 emit() 方法调用推送到 UCXTransmitter 对象(输出端口)中的队列的数据将使用 UCX 发送到网络。

在这些调用期间,系统会测量统计信息,执行监视器(如果有),并执行控制器(如果有)。

重要的是要注意,tick 代码小组件 NVTX 注释不涵盖 router->syncInbox(entity); 和 router->syncOutbox(entity);。这意味着 NVTX 捕获的时间范围仅测量执行 Codelet::tick() 的持续时间。通过查看注释范围无法测量通过 UCXTransmitter/UCXReceiver 发送和接收数据的时间。

Q12:在对我的 Holoscan 应用程序进行性能分析期间,我观察到显着的延迟问题,这些问题对实时性能产生了负面影响。我整理了流水线中每个算子的时序分解,我在下面包含以供参考。

最初,我假设每个 Holoscan 算子都独立且并发地处理帧。但是,我的观察表明,整个流水线正在顺序处理每个帧,这对于我的实时要求来说似乎不是最优的。

目前,我的可视化组件仅实现了大约 15 fps,这低于我的性能目标。鉴于我的流水线的总执行时间约为 70 毫秒,我担心它可能每 70 毫秒只能处理一帧。

您能否提供有关 Holoscan 中调度器的实现和潜在优势(如 NVIDIA 文档中关于调度器的参考)的更详细信息?具体来说,我感兴趣的是了解是否以及如何利用调度器来解决我的性能问题。

这是我的流水线中每个算子的时序分解

  • Replayer: 24.145 毫秒

  • ImageProcessing: 18.289 毫秒

  • Preprocessor: 1.213 毫秒

  • Inference: 23.861 毫秒

  • Postprocessor: 0.275 毫秒

  • PostImageProcessing: 2.695 毫秒

  • Viz: 1.575 毫秒

A12: 以下调度器机制可能会影响您应用程序的性能

  1. 帧处理并行化:多线程调度器 (MTS) 和基于事件的调度器 (EBS) 旨在实现多个帧的并发处理。这些调度器允许在流水线中的其他算子仍在处理先前帧时,启动后续帧的处理。

  2. 延迟与吞吐量注意事项:重要的是区分应用程序性能上下文中的端到端延迟和吞吐量。虽然 MTS 和 EBS 可能会提高应用程序的整体吞吐量(定义为每单位时间处理的帧数),但它们不一定会降低端到端延迟。端到端延迟是指单个帧从源到接收器遍历整个流水线所需的时间。

  3. 当前调度器实现状态:请注意,MTS 和 EBS 当前正在进行优化。在当前状态下,与贪婪调度器相比,它们可能会表现出更高的延迟。

  4. 算子间依赖关系:至关重要的是要理解,流水线中的算子并非完全孤立地运行。流水线架构在算子之间结合了双缓冲区队列,并将调度条件应用于这些队列。这种设计在应用程序图中的相邻节点之间引入了数据依赖关系,这会影响整体执行流程和时序。

Q13:如何通过重用 GPU 内存来提高 VideoStreamRecorderOp 的性能?

A13:VideoStreamReplayerOp 可以重用 CUDA 设备缓冲区,并通过使用 BlockMemoryPool 避免为每个帧进行分配/释放。

VideoStreamReplayerOp 没有参数(例如分配器)来使用自定义分配器,即使使用者可以指定 entity_serializer

  • 当前的实现始终使用 holoscan::StdEntitySerializer 和 holoscan::StdComponentSerializer 以及 UnboundedAllocator,而不管使用者指定的 entity_serializer 参数如何。

  • VideoStreamReplayerOp 创建的张量(GPU 或 CPU)的存储类型取决于张量对象序列化到的输入视频文件。因此,在不更新 holoscan::StdComponentSerializer 实现的情况下,VideoStreamReplayerOp 不能盲目地使用需要内存存储类型、内存池大小等的特定内存池分配器。

Q14: 我在此应用程序中观察到一些 CUPVA-CUDA 互操作相关的延迟,这些延迟在我们 CUPVA 测试应用程序中不存在。 Holohub 应用程序和 CUPVA 测试应用程序之间的一个显着区别在于 CUDA 流创建的方法。

在 CUPVA 测试应用程序中,我们按如下方式创建 CUDA 流

复制
已复制!
            

cudaStreamCreateWithFlags(&cuStream, cudaStreamNonBlocking)

相比之下,Holohub 应用程序使用 CudaStreamPool,以这种方式创建

复制
已复制!
            

const std::shared_ptr<CudaStreamPool> cuda_stream_pool = make_resource<CudaStreamPool>("cuda_stream", 0, 0, 0, 1, 5); // or const std::shared_ptr<CudaStreamPool> cuda_stream_pool = make_resource<CudaStreamPool>("cuda_stream", 0, cudaStreamNonBlocking, 0, 1, 5);

然后使用以下方式在 compute API 中检索 CUDA 流

复制
已复制!
            

auto cudaStream = cuda_stream_handler_.get_cuda_stream(context.context());

我想澄清以下几点

  1. 当调用 get_cuda_stream(..) 时,将使用默认流,还是将是非默认流?

  2. 鉴于我们目前在 CUPVA 中缺乏对其的支持,是否有方法确保不使用池中的默认 CUDA 流?

A14:重要的是要知道,GXF 和 Holoscan 中的 CUDA 流管理目前正处于发展状态。

在当前的 Holoscan 实现中,CUDA 流通过 holoscan::CudaStreamHandler 类进行管理。此类提供实用程序方法,以使用 CudaStreamPool 定义算子规范。

要使用非默认流,应用程序的 compose() 方法应按如下方式创建 cuda_stream_pool

复制
已复制!
            

const std::shared_ptr<CudaStreamPool> cuda_stream_pool = make_resource<CudaStreamPool>("cuda_stream", 0, cudaStreamNonBlocking, 0, 1, 5);

请注意,为 flag 参数使用 1cudaStreamNonBlocking 可确保使用非默认流。

为了在应用程序工作流中正确创建 CUDA 流并在算子之间共享 CUDA 流

  1. 在应用程序的 compose() 方法中

    • 创建 CudaMemoryPool 并将其作为参数传递给工作流图中的算子。

  2. 对于工作流中的每个算子

    • 在算子类中定义 holoscan::CudaStreamHandler 成员变量(例如,cuda_stream_handler_)。

    • setup(OperatorSpec& spec) 方法中调用 cuda_stream_handler_.define_params(spec);

    • compute() 方法中,使用以下调用

      • cuda_stream_handler_.from_message(context.context(), in_message); 从输入消息中检索 CUDA 流信息。

      • cuda_stream_handler_.get_cuda_stream(context.context()); 获取 CUDA 流。

      • cuda_stream_handler_.to_message(out_message); 将当前使用的 CUDA 流设置为输出消息,以供后续算子使用。

关于您关于由于 CUPVA 中缺乏支持而强制不使用池中默认 CUDA 流的特定问题,有两种潜在的方法

  1. 确保您的应用程序使用适当的 CUDA 流池配置。

  2. 在应用程序/算子级别实施错误处理或异常,以防止使用默认 CUDA 流。

值得注意的是,VideoReplayerOp 当前不支持此 CUDA 流处理。正在考虑在未来的更新中支持它以及 CUDA 内存池支持。

Q15:我正在寻求了解 Holoscan 框架内采用的内存分配策略。具体来说,我想澄清以下几点

  1. 如何在 Holoscan 的不同组件之间分配内存?

  2. 内存分配相对于 compute() 方法执行的时序是什么?内存是在以下情况下分配的:a) 每次调用 compute() 时,还是 b) 在执行开始时分配一次,然后重用?

  3. Holoscan 操作中使用了哪些类型的 CUDA 内存?具体来说:a) 是否使用了固定内存? b) 是否使用了 CUDA 托管内存? c) 所有内存交换是否都保留在设备 (GPU) 内存中?

A15:内存分配可以执行一次并重用,也可以在每次 compute 调用时单独分配。这取决于用户如何编写 compute 方法。我们提供了一个 BlockMemoryPool 分配器类,该类允许在每次调用时重用相同的内存块。类似地,可以使用 CUDA 流和异步内存分配调用 (CudaStreamPool)。我们希望在未来几个月内重构以使这些比现在更容易使用,但现在已经具备了这种能力。 BlockMemoryPool 当前仅使用设备内存。有一个 UnboundedAllocator 可以在以下三个位置之一分配

  • 系统内存(即 C++ new/delete)

  • 固定主机内存 (cudaMallocHost / cudaFreeHost)

  • 设备内存 (cudaMalloc / cudaFree)

Q16: 我正在运行内窥镜工具跟踪应用程序,其配置将计算和图形操作分离到两个不同的 GPU 上。我对这些 GPU 之间的数据传输机制有疑问

  1. 在将数据从计算 GPU 传输到图形 GPU 时,是否显式使用了 memcpy?

  2. 在我对 nsys profiler 报告的分析中,我观察到了 MemcpyDToHMemcpyHToD 操作。这让我质疑 GPU 间数据传输是否实际上是通过主机系统路由的。

A16:工具跟踪后处理正在进行设备到主机的复制 此处此处 。当应用程序在单个 GPU 上运行时,也会执行这些操作。 Holoviz 没有进行任何设备到主机的操作,无论是单 GPU 还是多 GPU。

Q1: 如何使用 Visual Studio Code 调试 Holoscan SDK 示例和测试?

A1: 您可以使用

复制
已复制!
            

./run vscode

命令在开发容器中启动 VSCode。配置 CMake,构建源代码,并使用“运行和调试”视图启动调试会话。

-j <# of workers>--parallel <# of workers> 可用于指定在构建过程中要运行的并行作业数。有关更多信息,请参阅 ./run vscode -h 中的说明。

Q2: 如何开始调试我的 Holoscan 应用程序?

有关在 Holoscan 仓库中调试应用程序,请参阅 调试部分。有关在 HoloHub 中调试应用程序,请参阅 HoloHub 教程,了解使用 Visual Studio Code 或 GDB 等其他工具设置调试的策略。

Q3: 是否可以在 Holoscan SDK 中同时调试 C++ 和 Python 组件?

A3:是的,您可以使用 VSCode 中的 Python C++ 调试器扩展同时调试 C++ 和 Python 组件。有关更多信息,请参阅 Holoscan SDK 用户指南中的 调试 部分。

Q4: 当我的应用程序崩溃时,如何分析核心转储文件?

A4:将 gdb 命令与您的应用程序和核心转储文件一起使用,例如,

复制
已复制!
            

gdb <application> <coredump_file>

这将允许您检查堆栈跟踪和其他调试信息。

Q5: 如果未生成核心转储,我该怎么办?

A5: 通过设置启用核心转储

复制
已复制!
            

ulimit -c unlimited

并配置 core_pattern value。如果在 Docker 容器中工作,您可能需要在主机系统上执行此操作。有关更多信息,请参阅 Holoscan 用户指南 中的调试部分。

Q6: 如何使用 UCX 调试分布式应用程序?

A6: 设置 UCX_HANDLE_ERRORS 环境变量以控制 UCX 在崩溃期间的行为。选项包括打印回溯、附加调试器或冻结应用程序。有关更多信息,请参阅 Holoscan 用户指南 中的调试部分。

UCX_LOG_LEVEL 环境变量可以设置为“info”或更高级别,以查看有关使用的 UCX 传输的更详细信息(UCX 日志记录的默认级别为“warn”)。可用的完整 UCX 日志记录级别集对应于 此处 的列表。

Q7: Holoscan 中有哪些工具可用于分析 Python 应用程序的性能?

A7: 您可以使用 pyinstrument、pprofile、yappi、cProfile 或 line_profiler 等工具。每种工具都有不同的优势,可能更适合您的特定需求。有关更多信息,请参阅 Holoscan 用户指南中的 Python 调试部分

每种分析器都有其优势,并且适用于不同的调试场景,从高级概述到详细的逐行分析。请在下面找到更多详细信息

  1. pyinstrument

    • 调用堆栈分析器,突出显示性能瓶颈

    • 直接在终端中提供易于理解的输出

    • 多线程感知,适用于与多线程调度器一起使用

    • 可视化执行树,显示在每个函数中花费的时间

  2. pprofile

    • 行粒度分析器

    • 线程感知且确定性

    • 为每行代码提供详细的统计信息

    • 显示每行代码的命中计数、每次命中的时间和占总时间的百分比

  3. yappi

    • 跟踪分析器,它是多线程、asyncio 和 gevent 感知的

    • 可以处理 Holoscan 应用程序中复杂的线程场景

    • 提供跨不同线程的方法的命中计数

    • 需要设置上下文 ID 回调以实现准确的线程识别

  4. cProfile

    • 内置于 Python 的确定性性能分析模块

    • 提供函数调用和花费时间的高级概述

    • 易于使用,设置最少

    • 非常适合识别哪些函数总体上花费时间最多

  5. line_profiler

    • 提供特定函数的逐行性能分析

    • 提供函数内每一行的详细计时信息

    • 可用于精确定位导致性能问题的确切行

    • 需要将 @profile 装饰器添加到感兴趣的函数

Q8:如何衡量我的 Holoscan Python 应用程序的代码覆盖率?

A8:您可以使用

复制
已复制!
            

Coverage.py

来衡量代码覆盖率。

  • 使用 pip 安装它

  • 使用 coverage 运行您的应用程序

  • 使用 coverage report 或 coverage html 等命令生成报告

有关更多详细信息,请参阅 Holoscan 用户指南中的衡量代码覆盖率部分。

Q9:如何在我的 Python 应用程序中跟踪函数调用?

**A9:**您可以使用 trace 模块来跟踪函数调用。使用以下命令运行您的应用程序:

复制
已复制!
            

python -m trace --trackcalls

或在您的代码中以编程方式使用 trace 模块。

Q10:如何利用大型语言模型 (LLM) 来协助调试使用 Holoscan SDK 实现的复杂且耗时的任务?

A10:Holoscan SDK 允许逐步完成任务,并在完成任务的每个部分时从您那里获得反馈。这允许对复杂、耗时的过程进行逐步调试。有关 Holochat 链接,请参阅 Holochat-GPT,以询问有关构建和调试 Holoscan SDK 应用程序的问题并获得反馈。请注意,Holochat-GPT 不是保密的,并且可能没有使用最新的 Holoscan SDK 版本进行训练。有关最新的 API,请参阅Holoscan SDK 用户指南

Q11:如何在 Holoscan SDK 中使用 Python 的 `coverage.py`?

A4:在 Python 中,代码覆盖率测量和 Python 调试器都使用 sys.settrace()PyEval_SetTrace() 方法为当前线程注册跟踪方法。当 coverage.py 或 Python 调试器运行时,它会为跟踪 Python 代码执行调用这些方法(以及 threading.settrace 用于新创建的线程)。但是,当 Holoscan SDK 中的线程(GreedyScheduler/MultiThreadScheduler 的 worker)调用 Python 方法(例如 compute())时,这些线程不是从 Python 的主线程派生的,sys.settrace()(或 PyEval_SetTrace())对于这些线程未被正确调用。解决方案是

1. 捕获当前的跟踪方法(通过使用 sys.gettrace(),假设 CURR_TRACE_METHOD - 如果在调用 Application.run() 方法时存在)。2. 当调用 Python 方法(例如 Operator.compute()/Fragment.compose()/Operator.initialize()/Operator.start()/Operator.stop())时,获取当前的跟踪方法(通过使用 sys.gettrace())并调用 sys.settrace(CURR_TRACE_METHOD) 并将当前堆栈帧的 f_trace 设置为 CURR_TRACE_METHOD (当前堆栈帧可通过 inspect.currentframe() 获得),如果之前没有设置跟踪方法。

  • 可以通过存储 thread id -> <trace method> 映射(或使用线程局部变量)并检查跟踪方法是否已注册到当前线程来加速此过程。

Python 的 cProfile 模块使用 sys.setprofile() 而不是 sys.settrace()(因为 profile 方法是按方法调用的,这更有效),我们可以应用类似的方法来在 Holoscan 的 Python Operator 方法上启用性能分析器。

Q12:Holoscan SDK 如何协调多 Fragment 应用程序的命令行参数?

A12: CLI 参数(例如 –driver、–worker、–fragments)由 Application 类解析,其余参数作为 app.argv 属性提供。

复制
已复制!
            

import argparse import sys from holoscan.core import Application class MyApp(Application): def compose(self): pass if __name__ == "__main__": app = MyApp() print("sys.argv:", sys.argv) print("app.argv:", app.argv) parser = argparse.ArgumentParser() parser.add_argument("--input") args = parser.parse_args(app.argv[1:]) print("args:", args) app.run() # $ python cli_test.py --address 12.3.0 --input a # sys.argv: ['cli_test.py', '--address', '12.3.0', '--input', 'a'] # app.argv: ['cli_test.py', '--input', 'a'] args: Namespace(input='a')

Q13:为什么 Inference Operator 拒绝具有 5 维输入(batch、temporal_dim、channels、width、height)的 CNN-LSTM 模型的输入形状? A13:在 Holoscan SDK v2.4 及更早版本中,InferenceOp 仅支持 2 到 4 之间的秩。

Q14:我正在尝试使用 NVIDIA NSight Systems 在容器中分析 Holoscan 应用程序。我正在遵循 https://github.com/nvidia-holoscan/holohub/blob/main/doc/developer.md 上的文档,并使用最近检出的 Holohub 和 Holoscan v2.1 NGC 镜像。

我的过程如下

  1. 启动启用 NSight 性能分析的开发容器

复制
已复制!
            

./dev_container launch --nsys_profile

  1. 使用 NSight 性能分析启动内窥镜工具跟踪应用程序

复制
已复制!
            

./run launch endoscopy_tool_tracking python --nsys_profile

但是,我遇到以下错误

复制
已复制!
            

ERROR: For Nsight Systems profiling the Linux operating system's perf_event_paranoid level must be 2 or less. See https://docs.nvda.net.cn/nsight-systems/InstallationGuide/index.html#linux-requirements for more information.

如何修复它?

A14:

复制
已复制!
            

sudo sh -c 'echo 2 >/proc/sys/kernel/perf_event_paranoid'

命令需要在主机系统上执行,而不是在容器内。

  • 检查当前值

复制
已复制!
            

cat /proc/sys/kernel/perf_event_paranoid

  • 要临时将值更改为 2(允许非特权用户进行内核性能分析)

复制
已复制!
            

sudo sh -c 'echo 2 >/proc/sys/kernel/perf_event_paranoid'

  • 要永久更改,请编辑 /etc/sysctl.conf:添加或修改行

复制
已复制!
            

update /etc/sysctl.conf

  • 然后应用更改

复制
已复制!
            

sudo sysctl -p

  • 如果您需要允许所有用户使用几乎所有事件,您可以将值设置为 -1 而不是 2。

  • 这些值及其含义

    • -1:允许所有用户使用(几乎)所有事件

    • 0 或更高:禁止没有 CAP_SYS_ADMIN 的用户使用 ftrace 函数跟踪点

    • 1 或更高:也禁止没有 CAP_SYS_ADMIN 的用户访问 CPU 事件

    • 2 或更高:也禁止没有 CAP_SYS_ADMIN 的用户进行内核性能分析

  • 进行更改后,您可能需要重新启动您的应用程序或依赖于这些性能事件的服务。

  • 有关更多详细信息,请参阅 perf_event_open 手册页

请记住,降低此值会增加非特权用户对性能数据的访问权限,这在某些环境中可能是一个安全问题。在进行此类更改之前,请务必考虑安全隐患。有关更多信息,请参阅 NVIDIA Nsight Systems 安装指南

Q15:我正在开发一个利用两个不同处理图的应用程序

  1. VideoReplayerOp -> HolovizOp

  2. CameraOp -> HolovizOp

第一个图显示用于摄像头观察的源视频,而第二个图将 AI 处理应用于增强摄像头输入。目前,我正在使用两个单独的 Holoviz 实例,用于源视频(图 1)的实例专门使用第二个显示器 (DP-4)。

我在应用程序终止期间遇到了一个问题:当按下“Esc”退出时,主屏幕上的主 Holoviz 实例按预期关闭,但源视频继续在辅助屏幕上播放。虽然我可以使用“Ctrl+C”强制退出应用程序,但我正在寻求更优雅的解决方案以进行适当的终止。

是否有推荐的方法来优雅地关闭整个应用程序?

A15:为了解决您关于使用多个 Holoviz 实例优雅终止应用程序的问题,让我们首先了解 ESC 键在 Holoviz 中的功能

  1. ESC 键行为

    • Holoviz 监视通过 ESC 键发出的窗口关闭请求。

    • 按下时,它通过将布尔调度项设置为 false 来停用关联的 HolovizOp。

  2. 终止场景

    • 单个 HolovizOp:按下 ESC 键关闭整个应用程序。

    • 多个 HolovizOps:ESC 仅终止特定的 Holoviz 实例,而其他实例仍在运行。

建议的解决方案

为了实现所有 Holoviz 实例之间的同步终止

  1. 创建一个共享的布尔调度条件。

  2. 对于应用程序中的每个 HolovizOp

    • 将此条件设置为常规执行条件。

    • 重要的是,还要将其设置为 window_close_condition 参数(注意:此参数在 v2.7 之前的版本中名为 window_close_scheduling_term)。

Q16:我正在尝试使用 Holoviz Python operator 中的 render_buffer_output,但我收到以下错误

复制
已复制!
            

[error] [entity.hpp:90] Unable to find component from the name 'render_buffer_output' (error code: 24)

A16:您收到此错误的原因是 HolovizOp 返回一个 gxf::VideoBuffer,Holoscan SDK 中的 Python 尚不支持它。

Q17:我正在使用带有 MST 板的 AGX Orin,带有 2 个显示器 - DP-0.1(触摸屏)和 DP-0.2(外部主显示器)。我正在尝试强制 Holoviz 在显示器 DP-0.2 上显示,方法是设置 use_exclusive_display = Truedisplay_name = "DP-0.2"。但这会导致以下错误

复制
已复制!
            

[info] [exclusive_window.cpp:125] ____________________ [info] [exclusive_window.cpp:126] Available displays : [info] [exclusive_window.cpp:129] ADA (DP-0.1) [info] [exclusive_window.cpp:129] LG Electronics LG ULTRAWIDE (DP-0.2) [info] [exclusive_window.cpp:134] [info] [exclusive_window.cpp:135] Using display "LG Electronics LG ULTRAWIDE (DP-0.2)" [info] [exclusive_window.cpp:148] X server is running, trying to acquire display [error] [context.cpp:56] VkResult -13 - unknown [error] [gxf_wrapper.cpp:57] Exception occurred when starting operator: 'holoviz' - Failed to acquire display from X-Server.

作为潜在的修复,我已在 nvidia-settings 中为合成器禁用了主显示器,但应用程序仍然崩溃,并出现以下错误

复制
已复制!
            

[error] [context.cpp:56] /workspace/holoscan-sdk/modules/holoviz/thirdparty/nvpro_core/nvvk/swapchain_vk.cpp(172): Vulkan Error : unknown [error] [context.cpp:56] /workspace/holoscan-sdk/modules/holoviz/thirdparty/nvpro_core/nvvk/swapchain_vk.cpp(172): Vulkan Error : unknown [error] [context.cpp:56] /workspace/holoscan-sdk/modules/holoviz/thirdparty/nvpro_core/nvvk/swapchain_vk.cpp(172): Vulkan Error : unknown [error] [gxf_wrapper.cpp:57] Exception occurred when starting operator: 'holoviz' - Failed to update swap chain.

A17:消息“[info] [exclusive_window.cpp:161] Using display mode 1920x1080 60.000 Hz”指示的进度是一个积极的信号。但是,Vulkan 交换链创建失败并出现未知错误令人担忧。虽然我们定期使用独立 GPU (dGPU) 测试独占显示,但很少使用集成 GPU (iGPU) 进行测试。过去,我们遇到过 iGPU 上 Vulkan 功能的问题。

另一种选择是使用全屏模式,已知全屏模式可在 iGPU 上工作。重要的是要注意,Holoviz 始终在主显示器上以全屏模式打开,并且当前不支持显示器名称选择。

鉴于他们想要用于 Holoviz 的显示器似乎是主显示器,您可以尝试设置 fullscreen = True 而不是 use_exclusive_display = True

Q18:如何在 Holoscan SDK 的 InferenceOp 中使用 CuPy 数组? A18: CuPy 和 Holoscan SDK 都支持 _cuda_array_interface,以促进库之间的无缝数组集成。有关在 Holoscan SDK 应用程序中使用 CuPy 数组的详细信息,请参阅 HoloHub 库集成教程 CuPy 部分

Q19:在我的网络环境中使用 Holoscan Packager 时,我遇到了这些错误

复制
已复制!
            

curl: (6) Could not resolve host: github.com. Failed to establish a new connection:: [Errno -3] Temporary failure in name solution...

A19:要解决这些错误,请编辑 /etc/docker/daemon.json 文件以包含 dnsdns-search 字段,如下所示

复制
已复制!
            

{ "default-runtime": "nvidia", "runtimes": { "nvidia": { "args": [], "path": "nvidia-container-runtime" } }, "dns": ["IP-1", "IP-n"], "dns-search": ["DNS-SERVER-1", "DNS-SERVER-n"] }

您可能需要咨询您的 IT 团队,并将 IP-xDNS-SERVER-x 替换为提供的值。

Q20:尝试使用 RMMAllocator 时,我看到以下错误

运行应用程序时,如果启动失败并记录了如下错误

复制
已复制!
            

[error] [rmm_allocator.cpp:74] Unexpected error while initializing RMM Allocator rmm_allocator: std::bad_alloc: out_of_memory: RMM failure at:bazel-out/k8-opt/bin/external/rmm/_virtual_includes/rmm/rmm/mr/device/pool_memory_resource.hpp:424: Maximum pool size exceeded

这表明主机和/或设备上请求的内存大小超过了可用内存。请确保您的设备支持指定的内存大小。另请检查指定的 device_memory_initial_sizedevice_memory_max_sizehost_memory_initial_sizehost_memory_max_size 的值是否使用了预期的单位(B、KB、MB、GB 或 TB)。

Q1:我可以在 Holoscan SDK 中使用 DLA 内核吗?

A1:在两种情况下,Holsocan SDK 可以支持深度学习加速器 (DLA) 内核。您可以配置您的应用程序以将某些推理任务卸载到 DLA 内核,以提高性能和效率。

  • 用户创建的引擎文件:如果 TensorRT 引擎文件是使用 –useDLACore=0 创建的,并且该引擎文件在 Holsocan SDK 推理框架中使用,那么它将使用 DLA(内核 0),因为 HoloInfer 只是引导执行到 TensorRT,它会自动拾取它。用户必须在 model_path_map 中提供引擎文件的路径,并在推理参数集中启用标志 is_engine_path=true

  • 如果用户通过 HoloInfer 创建引擎文件:HoloInfer 当前不支持使用任何 DLA 内核进行引擎创建(即使可用),并且默认使用 GPU。

Q2:如何从 Graph Composer 生成 HSDK 应用程序?

A2:在 Graph Composer 中,图节点(实体)基于 GXF Codelet/组件(来自 GXF 扩展),这些组件已在 Graph Composer 注册表中注册。目前,没有任何 Holoscan 运算符在此注册表中注册。但是,我们有一种将 Holoscan 运算符转换为 GXF 扩展的方法。请在下面找到示例代码

要生成或运行 Graph Composer 中描述的应用程序图(pipeline),我们需要一种将 GXF Codelet/组件导入为 Holoscan 运算符/资源的方法。目前,用户需要手动包装 GXF Codelet/资源以将其转换为 Holoscan 运算符/资源。

Q3:如何支持 React 等外部事件?

A3:关于参数标志,我们支持 GXF 的可选/动态参数标志(图 1),并且可以使用 spec.param(..., ParameterFlag::kDynamic);Operator::setup(OperatorSpec& spec) 方法中设置它(图 2)。但是,此参数标志主要用于包装 GXF Codelet。在实现 Holoscan Native 运算符时,没有针对参数值更改的运行时检查,允许我们更新参数值并在不设置参数标志的情况下使用它们(holoscan::Parameter<T> 仅充当使用 std::any 的值持有者)。

为了支持外部事件/控件(例如 Qt),我们可以实现一个处理事件和状态管理的 Holoscan 资源(GXF 组件)。这将允许 Holoscan 运算符根据事件更新状态并使用它,类似于 React 的状态管理方法。QtHoloscanVideo(图 3)可能是此类 Holoscan 资源的良好候选者。

作为参考,CudaStreamPool 是 Holoscan 资源(包装 GXF CudaStreamPool 组件)的一个示例,并且该对象作为参数传递给运算符(图 4)。但是,没有必要将 Holoscan 资源作为参数传递。相反,我们可以将它们提供给运算符(图 5)并通过 compute() 方法内部的 resource<T>() 方法访问它们(图 6)。在 Python 中,可以通过 op.resources 属性(类型为 dict[str, Resource])访问资源。

Holoscan 支持 C++ 的 Native Resource(并提供演示此功能的示例),但尚未实现 Native Python 资源。

鉴于这种情况,最好将其实现为 GXF 组件并将其公开为 C++/Python Holoscan 资源。这种方法将允许 QtVideoOp(在图 3 中)定义一个公共方法,用于从 Qt 应用程序逻辑访问事件/控制 Holoscan 资源对象(可能通过 QtVideoOp 中的 HoloscanVideo* 对象)。

image3.png

图 1

image4.png

图 2

image5.png

图 3

image6.png

图 4

image7.png

图 5

image8.png

图 6

Q5:我在 pybind11 中实现 PYBIND11_OVERRIDE_PURE 机制时遇到困难。具体来说,我正在尝试使用 Python 子类覆盖纯虚 C++ 方法,但它没有按预期工作。Python 子类似乎没有成功覆盖 C++ 方法。 A5:一种可能的修复方法是保留对 Python 对象的全局引用。请参阅此处提供的修复。

此代码解决了 pybind11 处理类继承和实例管理时的一个潜在问题。当使用 Python InstrumentedReceiverOperator 类模拟 C++ RoceReceiverOp 类时,会出现此问题。以下是情况分解

  1. 问题:当 Holoscan 应用程序运行时,它调用 RoceReceiverOp 中的方法,而不是预期的 InstrumentedReceiverOperator 方法。

  2. 原因:InstrumentedReceiverOperator 实例在应用程序执行期间似乎从 pybind11 的内部注册表中消失了。尽管期望将其传递给 C++ 应该维护其生命周期,但仍会发生这种情况。

  3. 调试尝试:跟踪实例注销的努力(通过 pybind11 的 deregister_instance() 中的断点)未成功,导致确切原因不明。

  4. 解决方法:为了防止 InstrumentedReceiverOperator 实例过早销毁,代码维护了对它的全局引用。

此解决方案确保模拟的类实例在整个应用程序生命周期中保持可用,从而允许正确的方法覆盖和执行。

Q1:在哪里可以找到有关 Holoscan SDK 的其他资源和支持?

A1:可以在以下位置找到其他资源和支持:

  • NVIDIA 开发者页面。有关更多信息,请参阅链接。

  • Holoscan SDK GitHub 存储库。请参阅链接。

  • NVIDIA 开发者论坛,用于社区支持和讨论。对于 Holoscan SDK 论坛,请参阅链接。

Q2:如何为 Holoscan SDK 做出贡献?

A2:Holoscan SDK 是开源的。您可以通过以下方式做出贡献:

  • 提交针对错误修复或新功能的拉取请求。有关如何贡献的更多详细信息,请参阅链接。

  • 参与 Holoscan SDK 论坛上的社区讨论

上一篇 视频 Pipeline 延迟工具
© 版权所有 2022-2024,NVIDIA。 上次更新时间:2025 年 1 月 27 日。