对pytorch的底层nccl库进行插桩

intro

本文记录了使用dlsym对系统内nccl库进行插桩,然后再pytorch的脚本上运行呈现。环境配置可见使用系统内NCCL环境重新编译Pytorch

插桩代码

cpp 复制代码
// nccl_instrument.c
#include <nccl.h>
#include <stdio.h>
#include <dlfcn.h>

// 定义一个函数指针来指向原始的 ncclBroadcast 实现
static ncclResult_t (*original_ncclBroadcast)(const void *, void *, size_t, ncclDataType_t, int, ncclComm_t, cudaStream_t) = NULL;

extern "C" ncclResult_t ncclBroadcast(const void *sendbuff, void *recvbuff, size_t count,
                                      ncclDataType_t datatype, int root, ncclComm_t comm, cudaStream_t stream)
{
    // 加载原始的 ncclBroadcast 函数
    if (!original_ncclBroadcast)
    {
        original_ncclBroadcast = (ncclResult_t(*)(const void *, void *, size_t, ncclDataType_t, int, ncclComm_t, cudaStream_t))dlsym(RTLD_NEXT, "ncclBroadcast");
        if (!original_ncclBroadcast)
        {
            fprintf(stderr, "Error loading original ncclBroadcast: %s\n", dlerror());
            return ncclSystemError;
        }
        else
        {
            printf("Successfully change the Point!\n");
        }
    }

    // 打印广播操作的信息
    printf("NEW![Instrumentation] ncclBroadcast called with count: %zu, root: %d\n", count, root);

    // 调用原始的 ncclBroadcast 函数
    return original_ncclBroadcast(sendbuff, recvbuff, count, datatype, root, comm, stream);
}

这是一个简单的对广播的插桩。

需要先把他编译为动态链接库(这里需要手动链接一下cuda的相应lib和include地址):

cpp 复制代码
 g++ -shared -fPIC -o libnccl_instrument.so nccl_instrument.cpp -L/usr/local/cuda/lib64 -lnccl -lcudart -I/usr/local/cuda/include

完事后可以先用一个test测试一下::

cpp 复制代码
// test_nccl.c
#include <nccl.h>
#include <stdio.h>
#include <cuda_runtime.h>

int main()
{
    ncclComm_t comm;
    int size = 1; // 单机单 GPU 时可以设置为 1
    int rank = 0; // 当前进程的 rank 为 0

    // 初始化 NCCL 通信
    ncclUniqueId id;
    ncclGetUniqueId(&id);
    ncclCommInitRank(&comm, size, id, rank);

    // 分配 GPU 内存
    int *sendbuff, *recvbuff;
    cudaMalloc((void **)&sendbuff, sizeof(int) * size);
    cudaMalloc((void **)&recvbuff, sizeof(int) * size);

    // 假设 root 节点是 0
    int root = 0;

    // 广播调用
    ncclBroadcast(sendbuff, recvbuff, size, ncclInt, root, comm, 0);

    // 释放资源
    ncclCommDestroy(comm);
    cudaFree(sendbuff);
    cudaFree(recvbuff);

    printf("Broadcast test completed.Successful!\n");
    return 0;
}

编译:

cpp 复制代码
nvcc -o test_nccl test_nccl.c -lnccl -lcudart

运行(使用PRE_LOAD环境变量优先链接自定义的库):

bash 复制代码
LD_PRELOAD=./libnccl_instrument.so ./test_nccl

输出如下:

bash 复制代码
Successfully change the Point!
NEW![Instrumentation] ncclBroadcast called with count: 1, root: 0
Broadcast test completed.Successful!

再拿一个简单的pytorch 脚本测试:

bash 复制代码
import os
import torch
import torch.distributed as dist

import ctypes
import os


# 设置通信环境
os.environ['MASTER_ADDR'] = 'localhost'
os.environ['MASTER_PORT'] = '29500'

# 初始化进程组
rank = 0  # 当前进程的 rank
world_size = 1  # 总进程数
dist.init_process_group("nccl", rank=rank, world_size=world_size)

# 创建张量
x = torch.zeros(6)  # 初始张量为全零

if torch.cuda.is_available():
    # 将张量移动到 GPU 上
    x = x.cuda()
    if rank == 0:
        # 只有 rank 0 初始化张量
        x = torch.arange(1, 7).float().cuda()

    # 广播张量,从 rank 0 广播到所有进程
    dist.broadcast(x, src=0)

    # 打印广播后的结果
    print(f"Rank {rank} broadcasted tensor: {x}")

运行的时候我试了好久,奇怪的是如果只是指定:

bash 复制代码
LD_PRELOAD=/mnt/d/Ubuntu_Code/nccl_PI/libnccl_instrument.so  python PI_test.py

就会报错,会发现链接器找不到broadcast符号,但是在c代码里边就不会,如果没有同时 PRELOAD 原始 NCCL 库,dlsym 会因为找不到符号而失败,导致 undefinedsymbol:ncclBroadcast。

我怀疑是:动态链接器无法自动加载原始 NCCL 库 ,PyTorch 的动态加载机制可能不依赖 ld.so 自动加载原始 NCCL 库。必须手动通过 LD_PRELOAD 明确加载 NCCL。改为下面的指令执行:

bash 复制代码
LD_PRELOAD=/mnt/d/Ubuntu_Code/nccl_PI/libnccl_instrument.so:/usr/local/cuda/lib64/libnccl.so python PI_test.py

结果如下:

bash 复制代码
Successfully change the Point!
NEW![Instrumentation] ncclBroadcast called with count: 6, root: 0
Rank 0 broadcasted tensor: tensor([1., 2., 3., 4., 5., 6.], device='cuda:0')
相关推荐
ATM00621 分钟前
人机协作系列(四)AI编程的下一个范式革命——看Factory AI如何重构软件工程?
人工智能·大模型·agent·人机协作·人机协同
读创商闻1 小时前
极狐GitLab CEO 柳钢——极狐 GitLab 打造中国企业专属 AI 编程平台,引领编程新潮流
人工智能·gitlab
kailp1 小时前
语言模型玩转3D生成:LLaMA-Mesh开源项目
人工智能·3d·ai·语言模型·llama·gpu算力
marteker1 小时前
弗兰肯斯坦式的人工智能与GTM策略的崩溃
人工智能·搜索引擎
无心水1 小时前
大语言模型零样本情感分析实战:无需机器学习训练,96%准确率实现指南
人工智能·机器学习·语言模型
来自于狂人1 小时前
AI大模型训练的云原生实践:如何用Kubernetes指挥千卡集群?
人工智能·云原生·kubernetes
橡晟7 小时前
深度学习入门:让神经网络变得“深不可测“⚡(二)
人工智能·python·深度学习·机器学习·计算机视觉
墨尘游子7 小时前
神经网络的层与块
人工智能·python·深度学习·机器学习
Leah01057 小时前
什么是神经网络,常用的神经网络,如何训练一个神经网络
人工智能·深度学习·神经网络·ai
倔强青铜37 小时前
苦练Python第18天:Python异常处理锦囊
开发语言·python