LLVM 项目博客

LLVM 项目新闻和来自战壕的第一手资料

面向数据科学的交互式 C++

面向数据科学的交互式 C++

在我们之前的博客文章 “使用 Cling 的交互式 C++” 中,我们提到探索式编程是降低问题复杂性的有效方法。本文将讨论为支持数据科学研究人员而开发的 Cling 的一些应用。特别是,交互式探查数据和接口使复杂的库和复杂数据更容易被用户访问。我们的目标是展示 Cling 在规模上的部分功能;Cling 的 eval 样式编程支持;与 Cling 相关的项目;以及展示交互式 C++/CUDA。

Eval 样式编程

Cling 实例可以通过其运行时访问自身。该示例创建一个 cling::Value 来存储递增变量 i 的执行结果。该机制可以进一步用于支持动态范围,扩展运行时的名称查找。

[cling]$ #include <cling/Interpreter/Value.h>
[cling]$ #include <cling/Interpreter/Interpreter.h>
[cling]$ int i = 1;
[cling]$ cling::Value V;
[cling]$ gCling->evaluate("++i", V);
[cling]$ i
(int) 2
[cling]$ V
(cling::Value &) boxes [(int) 2]

V 将表达式结果“装箱”,并在必要时提供扩展的生命周期。cling::Value 可用于将表达式值从解释器传送到编译代码。

[cling]$ ++i
(int) 3
[cling]$ V
(cling::Value &) boxes [(int) 2]

此机制引入了延迟到运行时的评估,它支持一些功能,增强了 C++ 语言的动态外观。

ROOT 数据分析包

高能物理 (HEP) 领域科学数据的存储、研究和可视化的主要工具是专门的软件包 ROOT。ROOT 是一组相互关联的组件,它们帮助科学家从数据存储和研究到在科学论文中发表时可视化。ROOT 在引力波、狮身人面像金字塔大空腔、大型强子对撞机发现希格斯玻色子等科学发现中发挥了重要作用。在过去 5 年中,Cling 帮助分析了 1 EB 物理数据,是 1000 多篇科学出版物的基础,并支持分布在百万 CPU 核心计算设施上的软件运行。

ROOT 使用 Cling 作为数据序列化的反射信息服务。C++ 对象以二进制格式存储,垂直排列。加载的数据文件的内容对用户可用,C++ 对象成为一等公民。

ROOT 的核心组件是 Cling 支持的 eval 样式编程。我们将其用于 HEP,以便轻松地检查和使用 ROOT 存储的 C++ 对象。Cling 使 ROOT 能够在打开文件时将可用对象名称注入名称查找中。

[root] ntuple->GetTitle()
error: use of undeclared identifier 'ntuple'
[root] TFile::Open("tutorials/hsimple.root"); ntuple->GetTitle() // #1
(const char *) "Demo ntuple"
[root] gFile->ls();
TFile**        tutorials/hsimple.root    Demo ROOT file with histograms
 TFile*        tutorials/hsimple.root    Demo ROOT file with histograms
  OBJ: TH1F    hpx    This is the px distribution : 0 at: 0x7fadbb84e390
  OBJ: TNtuple    ntuple    Demo ntuple : 0 at: 0x7fadbb93a890
  KEY: TH1F    hpx;1    This is the px distribution
  [...]
  KEY: TNtuple    ntuple;1    Demo ntuple
[root] hpx->Draw()

ROOT 框架在两个阶段将附加名称注入名称查找。首先,它通过标记 ntuple (#1) 的出现来构建一个无效的 AST,然后将其转换为 gCling->EvaluateT</*return type*/void>("ntuple->GetTitle()", /*context*/); 在下一个阶段,在运行时,ROOT 打开文件,读取其序言并通过 clang 中的外部名称查找功能注入名称。如果 ntuple->GetTitle() 接受参数,则转换将变得更加复杂。


图 1. 从根文件读取的 *px* 分布的交互式图。

笔记本中的 C++

章节作者: Sylvain Corlay,QuantStack

Jupyter Notebook 技术允许用户创建和共享包含实时代码、方程、可视化和叙述性文本的文档。它使数据科学家能够通过以简单易懂且可重复的方式共享其分析,轻松地交换想法或协作。语言无关性是 Jupyter 项目的关键设计原则,Jupyter 前端通过一个经过良好定义的协议与内核(运行代码的基础设施的一部分)通信。内核已为数十种编程语言开发,例如 R、Julia、Python、Fortran(通过基于 LLVM 的 LFortran 项目)。

Jupyter 的官方 C++ 内核依赖于 Xeus,这是内核协议的 C++ 实现,以及 Cling。使用内核协议的参考实现的优势是,许多功能可以免费获得,例如丰富的 mime 类型显示、交互式小部件、自动完成等等。

可以通过为所述类型提供 mime_bundle_repr 的重载来指定用户定义类型的丰富 mime 类型渲染,该重载将通过参数依赖查找进行拾取。


图 2. JupyterLab 中用户定义图像类型的图像内联渲染。

丰富的 mime 类型渲染的可能性是无穷无尽的,例如使用 HTML 表格对数据帧进行丰富显示,甚至可以使用 JavaScript 扩展在前端渲染的 mime 类型。

一个利用 Mathjax 进行丰富渲染的高级示例是 SymEngine 符号计算库。


图 3. 在 Jupyter 中使用 SymEngine 包进行丰富的 mime 类型渲染。

Xeus-cling 附带 Jupyter 小部件协议的实现,该协议允许与后端进行双向通信。


图 4. JupyterLab 中使用 C++ 内核的交互式小部件。

更复杂的小部件库已通过此框架启用,例如 xleaflet


图 5. JupyterLab 中使用 xleaflet 的 C++ 中的交互式 GIS。

其他功能包括针对标准库和第三方包的丰富的 HTML 帮助。


图 6. 通过在 JupyterLab 中键入 ?std::vector 来访问 std::vector 的 cppreference。

Xeus 和 Xeus-cling 内核最近被并入 Jupyter 的子项目,并受其行为准则和一般治理的约束。

xeus-cling 内核的未来计划包括:通过实现 Jupyter is_complete 消息来添加对 Jupyter 控制台界面的支持,目前缺少此功能;将 cling“点命令”添加为 Jupyter 魔法命令;以及支持最近添加到 Jupyter 内核协议的新调试器协议,这将使 C++ 内核能够使用 JupyterLab 可视化调试器。

另一个将交互式绘图功能带到 xeus-cling 的工具是 xvega,它处于开发的早期阶段,生成可以在笔记本中显示的 vega 图表。


图 7. xeus-cling 内核中的 xvega 绘图库。

CUDA C++

章节作者: Simeon Ehrig,HZDR

Cling CUDA 扩展将交互式 C++ 的工作流程带到了 GPU,而不会损失性能和与现有软件的兼容性。要执行 CUDA C++ 代码,Cling 会在编译器前端激活一个扩展,以了解 CUDA C++ 方言并创建第二个编译器实例,用于为 GPU 编译代码。


图 8. Cling 中的 CUDA/C++ 信息流。

与普通 C++ 模式一样,CUDA C++ 模式使用 AST 转换来支持交互式 CUDA C++ 或特殊功能,例如 Cling 打印系统。与用于主机代码的普通 Cling 编译器管道不同,设备编译器管道不使用主机管道的全部转换。因此,设备管道有一些特殊的转换。

[cling] #include <iostream>
[cling] #include <cublas_v2.h>
[cling] #pragma cling(load "libcublas.so") // link a shared library
// set parameters
// allocate memory
// ...
[cling] __global__ void init(float *matrix, int size){
[cling] ?   int x = blockIdx.x * blockDim.x + threadIdx.x;
[cling] ?   if (x < size)
[cling] ?     matrix[x] = x;
[cling] ?   }
[cling]
[cling] // launching a function direct in the global space
[cling] init<<<blocks, threads>>>(d_A, dim*dim);
[cling] init<<<blocks, threads>>>(d_B, dim*dim);
[cling]
[cling] cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, dim, dim, dim, &alpha, d_A, dim, d_B, dim, &beta, d_C, dim);
[cling] cublasGetVector(dim*dim, sizeof(h_C[0]), d_C, 1, h_C, 1);
[cling] cudaGetLastError()
(cudaError_t) (cudaError::cudaSuccess) : (unsigned int) 0

与普通 C++ 模式一样,CUDA 模式可以在 Jupyter Notebook 中使用。


图 9. Cling 中的 CUDA/C++ 信息流。

Cling 在 CUDA 模式下的一个特殊属性是,Cling 应用程序在第一次 CUDA API 调用时成为一个普通的 CUDA 应用程序。这使 CUDA SDK 能够与 Cling 配合使用。例如,您可以使用 CUDA 分析器 nvprof ./cling -xcuda 来分析您的交互式应用程序。 此 docker 容器可用于试验 Cling 的 CUDA 模式。

CUDA 模式的未来计划包括:支持完整的当前 CUDA API;重新定义 CUDA 内核;支持其他 GPU SDK,例如 HIP(AMD)和 SYCL(Intel)。

结论

我们认为交互式 C++ 的使用是为数据科学社区的研究人员开发的重要工具。Cling 使 ROOT 成为高能物理领域“首选”数据分析工具,涵盖从高效 I/O 到绘图和拟合的所有方面。交互式 CUDA 后端允许轻松集成研究工作流程,简化 C++ 和 CUDA 之间的通信。随着 Jupyter Notebook 成为数据分析师探索想法的标准方式,Xeus-cling 确保了每个 C++ 笔记本中都提供优秀的交互式 C++ 组件。

在下一篇博客文章中,我们将重点介绍 Cling 支持的交互式 C++ 之外的功能,特别是语言互操作性。

致谢

作者感谢 Sylvain Corlay、Simeon Ehrig、David Lange、Chris Lattner、Javier Lopez Gomez、Wim Lavrijsen、Axel Naumann、Alexander Penev、Xavier Valls Pla、Richard Smith、Martin Vassilev 对本文做出的贡献。

您可以在 https://root.cern/cling/https://compiler-research.org 上了解更多关于我们活动的信息。