13.7. 源代码
13.7.1. 代码风格
我们有意在Open MPI代码库中不设置过多的编码规范。
13.7.1.1. 所有语言
4个空格制表符。不多不少。
绝对不要使用实际的制表符;始终使用空格。无论是emacs还是vim都有隐藏功能,可以在你按下
键时自动使用空格。这样无论在不同的浏览器中如何设置制表符显示,代码看起来都是一致的。
13.7.1.2. C / C++
在进行常量相等或不相等比较时,始终将常量放在左侧。这是一种防御性编程:如果测试中存在拼写错误并遗漏了
!或=,将会产生编译器错误。例如:/* 正确做法 */ if (NULL == foo) { ... } /* 因为如果出现拼写错误(比如用=代替==),这将产生编译错误而非难以察觉的bug */ if (NULL = foo) { ... }
更防御性的编程:始终使用大括号
{ }包裹代码块,即使只有一行代码。例如:/* 这样做 */ if (whatever) { return OMPI_SUCCESS; } /* 不要这样做 */ if (whatever) return OMPI_SUCCESS;
从Open MPI 1.7版本开始,Open MPI需要一个符合C99标准的编译器。
现在允许(并推荐)使用C++风格的注释。
允许(且推荐)使用C99风格的混合声明。
始终将
作为你的第一个#include文件,其中_config.h 可以是ompi、oshmem或opal——这取决于你正在编写的层级。只有极少数情况下不需要这样做(例如某些特殊的Windows场景)。但在99.9999%的情况下,这个文件应该被首先包含,以便在必要时影响系统级的#include文件。文件名和符号必须遵循前缀规则(参见[邮件讨论](http://www.open-mpi.org/community/lists/devel/2009/07/6389.php)):
文件名必须以
作为前缀。_ 公共符号在组件中必须使用前缀
,其中_ _ 可以是mca、ompi、oshmem或opal之一。请注意mca过去是最常用的前缀,但现在相比其他前缀已不再受欢迎。如果不确定某个符号是否为公共符号,为安全起见请添加前缀。非公开符号必须声明为
static或采取其他方式使其不出现在全局作用域中。
始终 #define宏,即使是逻辑值。
GNU的方式是当宏为"真"时使用
#define定义,当宏为"假"时使用#undef取消定义。在Open MPI中,我们始终使用
#define将逻辑宏定义为0或1——我们从不使用#undef取消定义。这样做是出于防御性编程的考虑:如果你只是检查预处理宏是否定义(通过
#ifdef FOO或#if defined(FOO)),当宏名称拼写错误时,编译时不会收到任何警告。然而,如果你使用逻辑测试#if FOO但宏未定义(例如由于拼写错误),你将收到编译器警告或错误。设计原理
当拼写错误的宏名称隐藏在数千行代码中时,可能极难发现;我们将尽可能利用预处理器/编译器提供的所有帮助!
/* GNU Way - you will get no warning from the compiler if you misspell "FOO"; the test will simply be false */ #ifdef FOO ... #else ... #endif /* Open MPI Way - you will get a warning from the compiler if you misspell "FOO"; the result of the test is a different value than whether you spelled the macro name right or not */ #if FOO ... #else ... #endif
13.7.1.3. Fortran
我们没有针对Fortran的具体编码风格指南。请阅读源代码树中现有的Fortran代码,并尝试使用类似的风格。
13.7.1.4. Shell脚本编写
请阅读源代码树中的一些现有shell代码,并尝试使用类似的风格。
始终将评估的shell变量用引号括起来,以确保正确处理多标记值。
# This is bad if test $foo = bar; then # This is good if test "$foo" = "bar"; then
唯一的例外是,当将一个shell变量的值赋给另一个shell变量时,右侧不需要使用引号:
# 这样做无害但多余 foo="$bar" # 这样写就足够了,即使$bar包含多个标记 foo=$bar
不要在
test命令中使用==运算符——这是GNU的扩展功能,在BSD系统上可能导致可移植性问题。应该使用单个=运算符。# 错误用法 if test "$foo" == "bar"; then # 正确用法 if test "$foo" = "bar"; then
13.7.1.5. m4
我们没有针对m4语言(用于创建configure脚本)的具体编码风格指南。请阅读源代码树中现有的m4代码,并尝试使用类似的风格。
13.7.2. 树状布局
源代码树中有几个值得注意的顶级目录:
主要子项目:
oshmem: 顶层的OpenSHMEM代码库ompi: Open MPI代码库opal: OPAL代码库
config: 支持顶层configure脚本的M4脚本mpi.hetc: 一些杂项文本文件docs: Open MPI文档的源代码examples: 简单的MPI/OpenSHMEM示例程序3rd-party: 包含所需核心库的副本(通过Git克隆中的Git子模块或二进制压缩包提供)。注意
虽然可能被认为不太常见,但我们为第三方项目提供了二进制压缩包(而非Git子模块),这些项目包括:
Open MPI正常运行所需,以及
并非所有操作系统发行版都默认包含,以及
很少更新。
三个主要源代码目录(oshmem、ompi和opal)各自至少会生成一个顶级库,分别命名为liboshmem、libmpi和libopen-pal。这些库可以构建为静态库或共享库。某些目录树下还会生成可执行文件。
libopen-pal顶层库在内部构建时分为两部分:
libopen-pal_coreOPAL内部的核心组件,包含mpicc/mpirun等工具链接所需的基础源码和MCA框架。该"核心"库不会被安装。包含以下MCA框架:
backtrace,dl,installdirs,threads,timer包含
opal/class目录下的所有源代码以及opal/util目录下的大部分内容包含
opal/runtime目录中后缀为_core的文件
libopen-pal包含"核心"以及所有其他OPAL项目源代码。该库会被安装。
每个子项目源代码目录下都有类似(但不完全相同)的目录结构:
class: 本项目特有的类(使用OPAL类系统),类似C++的"类"include: 本项目专用的顶层包含文件mca: 本项目特定的MCA框架和组件runtime: 项目在运行时的启动和关闭tools: 本项目专用的可执行文件util: 随机实用代码
每个子项目中还有其他顶级目录,这些目录与该项目特定的逻辑和代码相关。例如,MPI API的实现可以在ompi/mpi/LANGUAGE下找到,其中LANGUAGE可以是c、fortran或java。
mca 目录树的布局有严格定义,其格式如下:
PROJECT/mca/FRAMEWORK/COMPONENT
明确说明:在mca目录树下,不允许存在不符合此模板的目录(下文将解释的base目录除外)。因此,只有框架和组件代码可以存放在mca目录树中。
也就是说,框架和组件名称必须是有效的目录名(以及C变量;稍后会详细说明)。例如,TCP BTL组件位于opal/mca/btl/tcp/。
名称base是保留关键字;不能存在名为base的框架或组件。名为base的目录专用于MCA和框架的实现。以下是几个示例(基于v5.0.x源码树):
# Main implementation of the MCA
opal/mca/base
# Implementation of the btl framework
opal/mca/btl/base
# Implementation of the sysv framework
oshmem/mcs/sshmem/sysv
# Implementation of the pml framework
ompi/mca/pml/base
在这些指定的目录下,框架和/或组件可以拥有任意的目录结构。
13.7.3. 符号可见性
*_DECLSPEC宏提供了一种方法来标注符号,以指示在编译动态共享对象文件(例如libmpi.so)时它们的预期可见性。这些宏是基于每个项目定义的:
Open MPI:
OMPI_DECLSPECOpen PAL:
OPAL_DECLSPECOpenSHMEM:
OSHMEM_DECLSPEC
这些宏展开为适当的编译器和平台标志,用于标记符号是否应在目标项目的库命名空间中显式公开。*_DECLSPEC属性用于声明符号在该库/DSO范围之外可见。例如,OMPI_DECLSPEC用于控制哪些符号在libmpi.so范围内可见。
注意
这完全与动态库编译相关,不适用于静态编译。
注意
这些宏最初是在Open MPI支持Windows时引入的(大约在Open MPI v1.0.0版本),其设计灵感来源于Windows的__declspec。虽然Open MPI已不再支持Windows平台,但这些符号可见性宏仍然保留了下来。