带有Cling的交互式C ++

2020-12-01 06:14:12

C ++编程语言用于许多数字密集型科学应用。性能与可靠的向后兼容性相结合,促使其在过去20年中用于许多研究软件代码。尽管功能强大,但是C ++经常被认为很难学习并且与快速的应用程序开发不一致。在开发过程中,漫长的编辑-编译运行周期会减慢探索和原型制作的速度。

Cling已成为一种公认的功能,可以为C ++开发人员提供交互性,动态互操作性和快速原型开发功能。 Cling支持完整的C ++功能集,包括模板,lambda和虚拟继承的使用。 Cling是一个交互式C ++解释器,建立在Clang和LLVMcompiler基础结构之上。解释器使交互式探索成为可能,并使C ++语言更受研究欢迎。

高能物理(HEP)领域中用于存储,研究和可视化科学数据的主要工具是专用软件包ROOT。 ROOT是一组相互连接的组件,可以帮助科学家从科学论文发表后的数据存储和研究到可视化。根在科学发现中起着重要作用,例如引力波,奇普斯金字塔的大空腔,大强子对撞机发现希格斯玻色子。在过去的5年中,Cling帮助分析了1种EB物理数据,作为1000多种科学出版物的基础,并支持在分布于数百万个CPU核心计算设施中运行的软件。

最近,我们启动了一个项目,旨在利用我们在交互式C ++,即时编译技术(JIT),动态优化和大规模软件开发方面的经验来极大地减少C ++和Python之间的阻抗失配。我们将推广Cling,以提供可靠,可持续的全学科C ++语言互操作性解决方案。我们目标的范围是:

启用可以在C ++和Python(以及其他语言,例如Julia和Swift)之间提供类似本机的动态运行时互操作性的功能

项目结果将集成到广泛使用的工具LLVM,Clang和Cling中。拟议工作的结果是一个提供C ++编译器即服务(CaaS)的平台,用于快速应用程序开发和计算性能。

本文的其余部分旨在演示Cling的设计和一些功能。想要跟进吗?您可以从conda上获取帮助

cling ****************** CLING *******************键入C ++代码,然后按Enter运行它**输入.q退出************************************************ [ cling] $ [cling] $ #include“ cling / Interpreter / Interpreter.h” [cling] $ gCling-> allowRedefinition(false)

我们将在本文的后续部分中解释这些命令的用途以及其他使用方式。

探索性编程(或快速应用程序开发)是一种了解项目需求的有效方法。减少问题的复杂性;并提供对系统设计和实施的早期验证。尤其是,交互式探查数据和接口使复杂的库和复杂的数据更易于访问,这对数据科学,计算科学和调试非常重要。这显着减少了开发过程中编辑运行周期所花费的时间。实际上,只有少数编程语言同时提供编译器和解释器,将它们翻译成机器代码,尽管是要解释还是编译语言是实现的属性。

能够进行探索性编程的语言倾向于具有解释器,这些解释器会缩短编译链接周期,而编译周期通常会明显降低性能。承认探索性编程用例的语言开发人员也可能会添加语法糖,但这主要是为了方便和简洁。通过使用即时(JIT)或提前(AOT)编译技术可以大大减轻性能损失。

对于本系列文章而言,解释C ++意味着启用C ++的探索性编程,同时通过JIT编译降低性能成本。图1显示了探索性编程的一个示例。确定形状,选择大小和颜色或与以前的设置进行比较变得很简单。看不见的编译链接循环有助于交互使用,从而允许使用一些质量上不同的方法来开发程序和提高生产率。

不要为不使用的东西付费-优先处理正确代码的性能。例如,为了提供错误恢复,不要惩罚用户在语法和语义上正确的C ++类型;和交互式C ++转换仅在必要时执行,并且可以禁用。

不惜一切代价重用Clang和LLVM –不要重造轮子。如果功能不可用,请尝试找到实现该功能的简约方法,并将其建议给LLVM社区进行审查。否则,找到最小补丁,即使以滥用API为代价,也可以满足要求。

连续功能交付–专注于最小功能,其在主要用例(ROOT)中的集成,在生产中的部署,重复。

学习和发展–体验用户体验。关于整体用户体验,没有形式规范或共识。应用从CINT继承的经验教训。

Cling接受部分输入,并确保编译器进程继续运行以对传入的代码进行操作。它包含一个API,可用于访问最近编译的代码块的属性。绑定可以在执行之前将自定义转换应用于每个块。 Cling按照图2中描述的数据流编排现有的LLVM和Clang基础结构。

简而言之:该工具通过交互式提示或允许增量处理输入(➀)的界面来控制输入基础结构。

Clang将可能包装在函数中的输入编译为AST(➂)。

必要时,可以对AST进行进一步转换,以附加特定行为(➃)。

例如,报告执行结果或其他与解释器相关的功能。一旦高级AST表示就绪,就将其发送以降低为LLVM特定的程序集格式LLVM IR(➄)。 LLVM IR是LLVM即时编译基础结构的输入格式。 Cling指示JIT运行指定的功能(➅),将它们转换为针对底层设备体系结构(例如Intel x86或NVPTX)的机器代码(MC)(➆,➇)。

C ++标准是针对编译器开发的,并未很好地涵盖交互性。在用户友好时,在全局范围内执行语句,报告执行结果和实体重新定义是三个最重要的功能。长时间运行的解释器会话很容易输入错误,因此必须确保完美的错误恢复。更高级的用例在运行时需要额外的灵活性,并且扩展了查找规则,以帮助评估样式。当C ++用作脚本语言时,有效的基于水印的代码删除非常重要。

Cling逐步处理C ++。增量输入由一个或多个C ++语句组成。 C ++不允许在全局范围内使用表达式。

[cling] #include [cling] #include [cling] std :: vector v = {1,2,3,4,5}; v [0] ++; [cling] std :: cout v = {1,2,3,4,5}; v [0] ++ ;; } //#1 void __unique_2(){std :: cout #include std :: vector v = {1,2,3,4,5}; void __unique_1(){v [0] ++ ;; } void __unique_2(){std :: cout [cling] std :: vector v = {1,2,3,4,5} //注意缺少的分号(std :: vector &){1, 2、3、4、5}

转换将根据特定实体的属性注入额外的代码,例如是否可复制,是包装临时对象还是无数组。通过提供“托管”存储,Cling可以报告有关不可复制或临时对象的信息。托管存储(cling :: Value)也用于在嵌入式设置中的已解释代码和已编译代码之间交换值。

名称重新定义是重要的脚本功能。对于每个基于笔记本的C ++,这也是必不可少的,因为每个单元格在某种程度上都是独立的计算。 C ++不支持实体的重新定义。

[cling] #include [cling] std :: string v(std :: string&)“” [cling] #include [cling] std :: vector vinput_line_7:2:19:错误:用不同类型重新定义'v':'std :: vector 'vs'std :: string'(aka'basic_string ,allocator >')std :: vector v ^ input_line_4:2:14:注意:以前的定义在这里std :: string v ^

Cling使用内联名称空间实现实体重新定义,并重新连接clanglookup规则,使更高的优先级用于较新的声明。此功能的完整说明已在CC 2020(ACM编译器构建会议)上的会议论文中发布,我们可以通过调用gCling-> allowRedefinition()启用它:

[cling] #include“ cling / Interpreter / Interpreter.h” [cling] gCling-> allowRedefinition()[cling] #include [cling] std :: vector v(std :: vector &){} [cling] #include [cling] std :: string v(std :: string&)“”

在交互模式下使用时,无效的C ++不会终止会话,而是会丢弃无效的代码。底层的clang进程将无效的AST节点保留在其内部数据结构中,以实现更好的错误诊断和恢复,并期望该进程将在发出诊断后不久结束。这个特定示例更具挑战性,因为它首先包含有效和无效构造。错误恢复应撤消内部结构(例如名称查找和AST)的大量更改。 Cling用于许多高性能环境;使用检查点不是一个可行的选择,因为它会引入正确代码的开销。

[cling] #include [cling] std :: vector v; v [0] .error_here; input_line_4:2:26:错误:成员引用基本类型'std :: __ 1 :: __ vector_base > :: value_type'(aka'int' )不是结构或联合std :: vector v; v [0] .error_here; ~~~~ ^ ~~~~~~~~~~

为了处理该示例,Cling将增量输入建模为事务。事务表示Clang内部数据结构变化的增量。 Cling侦听来自各种Clangcallbacks的事件,例如声明创建,反序列化和宏定义。此信息足以撤消更改并继续使用有效状态。实现非常复杂,在许多情况下,取决于输入声明的种类,需要额外的工作。

[cling] int * p = nullptr; * pinput_line_3:2:21:警告:null传递给需要非空参数的被调用者[-Wnonnull] int * p = nullptr; * p ^ [依附]

错误恢复和代码卸载的实现仍然具有粗糙的边缘,并且正在不断改进。

增量式交互式C ++假定会话长期存在,在会话中不仅可能发生语法错误,而且还会发生语义错误。如果我们想通过较小的调整重新执行相同的代码,那将带来一个复杂程度。

[cling] .L Adder.h //#1,类似于#include“ Adder.h” [cling] Add(3,1)// int Add(int a,int b){返回a-b; }(int)2 [cling] .U Adder.h //恢复#1 [cling]之前的状态。LAdder.h [cling] Add(3,1)// int Add(int a,int b) {返回a + b; }(int)4

在示例中,我们在.L meta命令中包含一个头文件;在.U中“取消包含”该文件,并在.L中“重新包含”该文件以重新读取修改后的文件。与在错误恢复情况下不同,Cling无法隔离降低机器代码的基础结构,而需要撤消clang CodeGen,llvm JIT和机器代码基础结构中的状态更改。实现此功能需要LLVM工具链中很大一部分的专业知识。

十多年来,Cling一直是支持交互式C ++的系统之一。Cling的可扩展性和快速原型制作功能对于高能物理研究人员而言至关重要,也是他们所依赖的许多技术的推动者。 Cling具有一些针对增量C ++所面临的挑战而量身定制的独特功能。我们在交互式C ++方面的工作一直在发展。在下一篇博客文章中,我们将重点介绍用于DataScience的交互式C ++。评估风格的编程;交互式CUDA;和C ++在笔记本中。

作者在此感谢Sylvain Corlay,Simeon Ehrig,David Lange,Chris Lattner,Wim Lavrijsen,Axel Naumann,Alexander Penev,Xavier Valls Pla,Richard Smith,Martin Vassilev的贡献。