pragma weak初遇

第一次见到pragma weak的位置
- 我在看nccl-test代码的时候,在src下面有很多集合操作的源代码,项目结构是这样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44.
├── build
│ ├── all_gather_perf
│ ├── all_reduce_perf
│ ├── alltoall_perf
│ ├── broadcast_perf
│ ├── gather_perf
│ ├── hypercube_perf
│ ├── reduce_perf
│ ├── reduce_scatter_perf
│ ├── scatter_perf
│ ├── sendrecv_perf
│ ├── timer.o
│ └── verifiable
│ └── verifiable.o
├── doc
│ └── PERFORMANCE.md
├── gotAcc.py
├── LICENSE.txt
├── Makefile
├── README.md
├── src
│ ├── all_gather.cu
│ ├── all_reduce.cu
│ ├── alltoall.cu
│ ├── broadcast.cu
│ ├── common.cu
│ ├── common.h
│ ├── gather.cu
│ ├── hypercube.cu
│ ├── Makefile
│ ├── nccl1_compat.h
│ ├── reduce.cu
│ ├── reduce_scatter.cu
│ ├── scatter.cu
│ ├── sendrecv.cu
│ ├── timer.cc
│ └── timer.h
└── verifiable
├── inexact_regress.cu
├── Makefile
├── verifiable.cu
├── verifiable.h
└── verifiable.mk - all_gather.cu, all_reduce.cu等等那一大堆文件里面,最后都会有这样一句话:
1
2
3
// 下面是all_reduce.cu里的例子 - 搜了一下,pragma weak是这样的意思,以下摘自GPT
GPT的答案
#pragma weak
是一个预处理指令,用于声明一个符号的弱链接(weak linkage)。这意味着,如果在链接时没有找到该符号的定义,链接器将使用该弱符号声明所提供的定义作为默认值。
这行代码的作用:
声明
ncclTestEngine
为弱符号(Weak Symbol):- 该行代码告诉编译器,
ncclTestEngine
是一个弱符号,并且它的默认值是sendRecvEngine
。这意味着,链接器会在链接时优先使用sendRecvEngine
作为ncclTestEngine
的定义,如果链接器找不到ncclTestEngine
的其他定义。
- 该行代码告诉编译器,
符号的替代:
ncclTestEngine
在其他地方可能会被定义为一个不同的符号,通常它是指向某个具体测试引擎实现的函数指针结构体。通过#pragma weak
,你可以在某些情况下使用sendRecvEngine
来代替ncclTestEngine
,特别是在没有明确提供ncclTestEngine
定义的情况下。
动态选择测试引擎:
- 如果其他地方(可能是链接时的设置)提供了对
ncclTestEngine
的定义,那么编译器会优先使用其他定义。否则,sendRecvEngine
将成为ncclTestEngine
的默认值,提供一组测试引擎实现。这样就可以根据实际情况灵活选择不同的引擎实现,而不需要修改代码中的其他部分。
- 如果其他地方(可能是链接时的设置)提供了对
具体场景:
默认引擎实现:
sendRecvEngine
作为默认的测试引擎包含了四个主要的函数指针:SendRecvGetBuffSize
、SendRecvRunTest
等。当ncclTestEngine
没有被其他地方定义时,sendRecvEngine
将会作为默认的实现使用。符号覆盖:
如果在程序的其他地方(可能是其他测试模块或者测试配置中)定义了ncclTestEngine
,那么编译器将使用该定义。如果没有定义ncclTestEngine
,那么sendRecvEngine
将自动成为ncclTestEngine
的实现。
为什么使用 #pragma weak
:
#pragma weak
通常用于以下场景:
- 库的可扩展性:允许不同的模块或库扩展并替换默认的符号定义,而不影响现有的代码结构。这种方式允许根据需要替换实现,例如切换到不同的测试引擎或不同的算法实现。
- 避免链接错误:如果某些符号没有明确的定义,而你又不想因为没有定义而导致链接失败,使用
#pragma weak
可以提供一个默认实现。
在nccl-test中的使用
- OK,现在我们知道了,pragma weak会在链接的时候给出一个实现,那这么多个弱链接,到底用哪个呢?
- 以sendrecv.cu为例,我们如果想要跑sendrecv,那么我们会输入类似这样的命令:
1
./build/sendrecv_perf -f 2 -b 1k -e 1k
- 那这个sendrecv_perf就是可执行文件,他的链接用到了哪些文件呢?通过查看makefile,我们可以得知使用了
- sendrecv.o
- common.o
- timer.o
- 来自 verifiable.mk 的目标文件(例如 verifiable.o)
- 那可以看到了,链接的时候就用到了sendrecv.o这一个,而通篇都没有对ncclTestEngine的强定义,那么就会使用sendrecv中给的弱链接定义啦