oneAPI GPU 优化指南 - 在 kernel 中进行 I/O

本文详细介绍了如何在SYCL的kernel中进行I/O操作,特别是使用stream类进行控制台打印和调试,强调了缓冲区大小和最大语句长度对输出的影响,以及在多工作项情况下的输出顺序不确定性。

本章节翻译by chenshusmail@163.com 原文:Doing I/O in the Kernel (intel.com)

打印语句是查看程序结果所需的最基本功能。在加速器上,打印语句出奇地困难, 而且在性能开销方面相当高昂。

SYCL* 提供了一些功能,以帮助使这项任务类似于标准的 I/O C/C++ 程序, 但是由于加速器的工作方式,您需要理解一些特性。从 SYCL* kernel 中无法进行文件 I/O。

SYCL* 提供了 stream 类,让您可以从 kernel 内部向控制台打印信息, 也提供了一种简单的方式来调试简单问题,而无需求助于调试器。 stream 类提供的功能非常类似于 C++ STL 的 ostream 类,其使用方法与 STL 类似。 下面我们描述如何使用 SYCL stream 类从 queue 的 kernel 中输出信息。

要使用该类,我们必须首先实例化它。stream 构造函数的签名如下:

stream(size_t BufferSize, size_t MaxStatementSize, handler &CGH);

构造函数接受三个参数:

  • BufferSize:在整个 kernel 范围内可能打印的字符总数

  • MaxStatementSize:对 stream 类的任何一次调用中的最大字符数

  • CGH:在 sycl::queue::submit 调用中对 sycl::handler 参数的引用

使用方法与 C++ STL 的 ostream std::cout 类非常相似。 需要打印的消息或数据通过适当的 operator<< 方法发送到 SYCL stream 实例。 SYCL 为所有内置数据类型(如 intchar 和 float)以及一些常见类 (如 sycl::nd_range 和 sycl::group)提供了实现。

以下是一个 SYCL stream 实例的使用示例:

void out1() {
  constexpr int N = 16;
  sycl::queue q;
  q.submit([&](auto &cgh) {
     sycl::stream str(8192, 1024, cgh);
     cgh.parallel_for(N, [=](sycl::item<1> it) {
       int id = it[0];
       /* Send the identifier to a stream to be printed on the console */
       str << "ID=" << id << sycl::endl;
     });
   }).wait();
} // end out1

使用 sycl::endl 类似于使用 C++ STL 的 std::endl ostream 引用 ——它用于插入新行以及刷新 stream。

编译并执行上述 kernel 将给出以下输出:

ID=0
ID=1
ID=2
ID=3
ID=4
ID=5
ID=6
ID=7
ID=8
ID=9
ID=10
ID=11
ID=12
ID=13
ID=14
ID=15

在选择适当的 BufferSize 和 MaxStatementSize 参数时必须小心。如果大小不足, 可能会导致语句不被打印,或者打印的信息比预期的少。请参考以下 kernel:

void out2() {
  sycl::queue q;
  q.submit([&](auto &cgh) {
     sycl::stream str(8192, 4, cgh);
     cgh.parallel_for(1, [=](sycl::item<1>) {
       str << "ABC" << sycl::endl;     // Print statement 1
       str << "ABCDEFG" << sycl::endl; // Print statement 2
     });
   }).wait();
} // end out2

编译并运行此 kernel 将给出以下输出:

ABC

第一条语句成功打印出来,因为要打印的字符数是 4(包括由 sycl::endl 引入的换行符), 并且最大语句大小(由 sycl::stream 构造函数的 MaxStatementSize 参数指定)也是 4。 然而,只有第二个语句的换行符被打印出来。

以下 kernel 显示了增加允许的最大字符大小的影响:

void out3() {
  sycl::queue q;
  q.submit([&](auto &cgh) {
     sycl::stream str(8192, 10, cgh);
     cgh.parallel_for(1, [=](sycl::item<1>) {
       str << "ABC" << sycl::endl;     // Print statement 1
       str << "ABCDEFG" << sycl::endl; // Print statement 2
     });
   }).wait();
} // end out3

编译并运行上述 kernel 将给出预期的输出:

ABC
ABCDEFG

上述示例使用了具有单个 work-item 的简单 kernel。更真实的 kernel 通常会包含多个 work-item。 在这些情况下,不能保证打印到控制台的语句的特定顺序,您应该预料到来自不同 work-item 的语句被交错输出。 请参考以下 kernel:

void out4() {
  sycl::queue q;
  q.submit([&](auto &cgh) {
     sycl::stream str(8192, 1024, cgh);
     cgh.parallel_for(sycl::nd_range<1>(32, 4), [=](sycl::nd_item<1> it) {
       int id = it.get_global_id();
       str << "ID=" << id << sycl::endl;
     });
   }).wait();
} // end out4

一次运行可以产生以下输出。

ID=0
ID=1
ID=2
ID=3
ID=4
ID=5
[snip]
ID=26
ID=27
ID=28
ID=29
ID=30
ID=31

当再次运行此程序时,我们可能会以完全不同的顺序得到输出,这取决于线程的执行顺序。

ID=4
ID=5
ID=6
ID=7
ID=0
ID=1
[snip]
ID=14
ID=15
ID=28
ID=29
ID=30
ID=31

sycl::stream 的输出是在 kernel 完成执行后打印的。在大多数情况下,这无关紧要。 然而,如果 kernel 出现故障或抛出异常,将不会打印任何语句。为了说明这一点, 请考虑以下 kernel,它引发了一个异常:

void out5() {
  int *m = NULL;
  sycl::queue q;
  q.submit([&](auto &cgh) {
     sycl::stream str(8192, 1024, cgh);
     cgh.parallel_for(sycl::nd_range<1>(32, 4), [=](sycl::nd_item<1> it) {
       int id = it.get_global_id();
       str << "ID=" << id << sycl::endl;
       if (id == 31)
         *m = id;
     });
   }).wait();
} // end out5

编译并执行上述代码会由于写入空指针而产生段错误。

Segmentation fault (core dumped)

没有任何打印语句实际上被打印到控制台。相反,您会看到一个关于段错误的错误消息。这与传统的 C/C++ stream 不一样。

上一章                                         主目录    上级目录                                                               下一章

按照你的方法进行排查,貌似还是不行:qsl@qsl:~$ sudo kubectl logs -n kube-system nvidia-device-plugin-daemonset-mgrhd | grep -i "strategy" "migStrategy": "none", "deviceListStrategy": "envvar", "deviceIDStrategy": "uuid" qsl@qsl:~$ sudo kubectl describe daemonset nvidia-device-plugin-daemonset -n kube-system | grep -i "strategy" qsl@qsl:~$ sudo kubectl patch daemonset nvidia-device-plugin-daemonset -n kube-system \ > --type='json' \ > -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args", "value": ["--strategy=legacy"]}]' daemonset.apps/nvidia-device-plugin-daemonset patched qsl@qsl:~$ sudo kubectl delete pods -n kube-system -l app=nvidia-device-plugin-daemonset No resources found qsl@qsl:~$ sudo kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.allocatable.nvidia\.com/gpu}{"\n"}{end}' desktop-dgqtj9o qsl qsl@qsl:~$ sudo kubectl delete pods -n kube-system -l name=nvidia-device-plugin-ds pod "nvidia-device-plugin-daemonset-2tc2b" deleted pod "nvidia-device-plugin-daemonset-mgrhd" deleted qsl@qsl:~$ sudo kubectl get pods -n kube-system -l name=nvidia-device-plugin-ds -w NAME READY STATUS RESTARTS AGE nvidia-device-plugin-daemonset-cq8sk 0/1 Error 1 (12s ago) 7s nvidia-device-plugin-daemonset-kvfgz 0/1 Error 1 (7s ago) 8s nvidia-device-plugin-daemonset-cq8sk 0/1 CrashLoopBackOff 1 (17s ago) 13s nvidia-device-plugin-daemonset-cq8sk 0/1 Error 2 (18s ago) 14s nvidia-device-plugin-daemonset-kvfgz 0/1 CrashLoopBackOff 1 (16s ago) 17s nvidia-device-plugin-daemonset-kvfgz 0/1 Error 2 (17s ago) 18s nvidia-device-plugin-daemonset-cq8sk 0/1 CrashLoopBackOff 2 (17s ago) 25s nvidia-device-plugin-daemonset-kvfgz 0/1 CrashLoopBackOff 2 (13s ago) 30s nvidia-device-plugin-daemonset-cq8sk 0/1 Error 3 (33s ago) 41s nvidia-device-plugin-daemonset-kvfgz 0/1 Error 3 (26s ago) 43s nvidia-device-plugin-daemonset-cq8sk 0/1 CrashLoopBackOff 3 (7s ago) 42s nvidia-device-plugin-daemonset-kvfgz 0/1 CrashLoopBackOff 3 (13s ago) 55s nvidia-device-plugin-daemonset-cq8sk 0/1 Error 4 (57s ago) 92s nvidia-device-plugin-daemonset-kvfgz 0/1 Error 4 (54s ago) 96s nvidia-device-plugin-daemonset-cq8sk 0/1 CrashLoopBackOff 4 (17s ago) 104s nvidia-device-plugin-daemonset-kvfgz 0/1 CrashLoopBackOff 4 (15s ago) 110s ^Cqsl@qsl:~$ sudo kubectl logs -n kube-system nvidia-device-plugin-daemonset-cq8sk Incorrect Usage. flag provided but not defined: -strategy NAME: NVIDIA Device Plugin - NVIDIA device plugin for Kubernetes USAGE: nvidia-device-plugin [global options] command [command options] [arguments...] VERSION: 1f8a4853 COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --mig-strategy value the desired strategy for exposing MIG devices on GPUs that support it: [none | single | mixed] (default: "none") [$MIG_STRATEGY] --fail-on-init-error fail the plugin if an error is encountered during initialization, otherwise block indefinitely (default: false) [$FAIL_ON_INIT_ERROR] --nvidia-driver-root value the root path for the NVIDIA driver installation (typical values are '/' or '/run/nvidia/driver') (default: "/") [$NVIDIA_DRIVER_ROOT] --pass-device-specs pass the list of DeviceSpecs to the kubelet on Allocate() (default: false) [$PASS_DEVICE_SPECS] --device-list-strategy value the desired strategy for passing the device list to the underlying runtime: [envvar | volume-mounts] (default: "envvar") [$DEVICE_LIST_STRATEGY] --device-id-strategy value the desired strategy for passing device IDs to the underlying runtime: [uuid | index] (default: "uuid") [$DEVICE_ID_STRATEGY] --gds-enabled ensure that containers are started with NVIDIA_GDS=enabled (default: false) [$GDS_ENABLED] --mofed-enabled ensure that containers are started with NVIDIA_MOFED=enabled (default: false) [$MOFED_ENABLED] --config-file value the path to a config file as an alternative to command line options or environment variables [$CONFIG_FILE] --help, -h show help (default: false) --version, -v print the version (default: false) 2025/10/29 08:48:34 Error: flag provided but not defined: -strategy qsl@qsl:~$,你要根据从底层到上层一次排查,保证底层稳定再去搭建上层这个思想去看。
10-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值