首页 > 系统相关 >使用 NVBit 进行内存访问跟踪指南

使用 NVBit 进行内存访问跟踪指南

时间:2024-10-19 23:17:40浏览次数:10  
标签:指南 int CUDA float NVBit 内存 size

使用 NVBit 进行内存访问跟踪指南

NVBit(NVIDIA Binary Instrumentation Tool) 是 NVIDIA 提供的一款轻量级、灵活的 GPU 二进制插桩框架,可以帮助开发者在不修改源代码的情况下,跟踪 CUDA 程序的内存访问。

具有以下特点:

  • 轻量级和高效:对应用程序的性能影响较小。
  • 灵活性:支持用户自定义插件,实现多种分析功能。
  • 透明性:无需修改应用程序源码或重新编译。
  • 适用性:支持 Maxwell(Compute Capability 5.0)及以上架构的 GPU。

NVBit 通过在程序运行时对 CUDA 内核的二进制代码(SASS)进行动态插桩,实现对内核执行的监控和分析。

NVBit 的工作原理

  1. 直接与 CUDA 驱动程序交互:
    NVBit 直接与 CUDA 驱动程序交互,处理已经编译成 SASS 机器代码的程序。它通过 LD_PRELOAD 机制在运行时注入库,使其能够在加载任何其他库之前加载指定的共享库。
  2. 应用程序二进制接口 (ABI):
    ABI 定义了调用者和被调用者之间的接口属性,例如寄存器的使用、参数传递方式等。NVBit 使用动态汇编器生成符合 ABI 的代码,以便能够将自定义 CUDA 设备函数注入到现有的应用程序中。
  3. CUDA API 回调:
    NVBit 为所有 CUDA 驱动程序 API 提供回调机制。这使得 NVBit 可以在 CUDA 程序调用任何 CUDA API 时截获这些调用,并插入自定义代码或进行分析。
    NVBit 还提供在应用程序启动和终止时的特定回调功能,可以在程序的整个生命周期中进行监控和干预。

NVBit 工具开发流程

  1. 开发 .cu 文件:
    使用 NVBit API 编写 CUDA 设备函数和回调函数。例如,在 .cu 文件中定义一个设备函数 incr_counter,用于在每次指令执行时计数。
  2. 编译 .cu 文件:

使用 NVCC 编译器将 .cu 文件编译成目标文件。
3. 链接生成共享库:

将编译好的目标文件与静态库 libnvbit.a 链接,生成一个共享库(通常是 .so 文件)。
示例生成的共享库:libmy_nvbit_tool.so。

https://blog.csdn.net/m0_63471305/article/details/139804027

1.下载地址:

https://github.com/NVlabs/NVBit/releases

解压 .bz2

tar -xvjf ./nvbit-Linux-x86_64-1.7.1.tar.bz2

如果直接git clone https://github.com/NVlabs/NVBit.git会只有两个txt、md两个文件

或者git clone https://github.com/CoffeeBeforeArch/nvbit_tools.git —>版本较旧

2.进入 NVBit 目录并设置环境变量:

cd nvbit-release
export NVBIT_ROOT=$(pwd)
export LD_LIBRARY_PATH=$NVBIT_ROOT:$LD_LIBRARY_PATH

3.进入tools里会有mem_trace目录:

mem_trace.cu是一个插槽功能的函数
编译:

 make ARCH=sm_80 #这里Makefile里是`ARCH?=ALL` 手动指定架构

编译成功后,将生成 mem_trace.so 插件文件,成为共享库。

4.编译cuda程序:

假设是矩阵相乘 matrixmul.cu :

nvcc -o matrixmul matrixmul.cu
#include <stdio.h>
#include <cuda_runtime.h>

// 矩阵维度
#define N 1024  // 矩阵大小 N x N

// CUDA内核,执行矩阵乘法
__global__ void matrixMulKernel(float* A, float* B, float* C, int n) {
    // 获取当前线程对应的行和列索引
    int row = blockIdx.y * blockDim.y + threadIdx.y;
    int col = blockIdx.x * blockDim.x + threadIdx.x;

    float value = 0.0;
    
    // 矩阵乘法的核心计算:C(row, col) = A(row, :) * B(:, col)
    if (row < n && col < n) {
        for (int k = 0; k < n; ++k) {
            value += A[row * n + k] * B[k * n + col];
        }
        C[row * n + col] = value;
    }
}

int main() {
    int size = N * N * sizeof(float);
    float *h_A, *h_B, *h_C;  // 主机内存
    float *d_A, *d_B, *d_C;  // 设备内存

    // 分配主机内存
    h_A = (float*)malloc(size);
    h_B = (float*)malloc(size);
    h_C = (float*)malloc(size);

    // 初始化矩阵A和B
    for (int i = 0; i < N * N; i++) {
        h_A[i] = 1.0f;  // 简单起见,将A和B初始化为全1矩阵
        h_B[i] = 1.0f;
    }

    // 分配设备内存
    cudaMalloc(&d_A, size);
    cudaMalloc(&d_B, size);
    cudaMalloc(&d_C, size);

    // 将主机内存拷贝到设备内存
    cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);
    cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);

    // 定义线程块和网格维度
    dim3 threadsPerBlock(16, 16);
    dim3 blocksPerGrid((N + threadsPerBlock.x - 1) / threadsPerBlock.x, 
                       (N + threadsPerBlock.y - 1) / threadsPerBlock.y);

    // 调用CUDA内核
    matrixMulKernel<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, N);

    // 将结果从设备内存拷贝到主机内存
    cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);

    // 验证结果
    bool success = true;
    for (int i = 0; i < N * N; i++) {
        if (h_C[i] != N) {
            success = false;
            break;
        }
    }
    if (success) {
        printf("矩阵相乘成功!\n");
    } else {
        printf("矩阵相乘失败!\n");
    }

    // 释放内存
    free(h_A);
    free(h_B);
    free(h_C);
    cudaFree(d_A);
    cudaFree(d_B);
    cudaFree(d_C);

    return 0;
}

5. 注入共享库

在运行时通过 LD_PRELOAD 机制将共享库注入到目标应用程序中。

LD_PRELOAD=nvbit-release/tools/mem_trace.so ./matrixmul

标签:指南,int,CUDA,float,NVBit,内存,size
From: https://blog.csdn.net/2303_77224751/article/details/143086239

相关文章

  • Java的重载和主要内存区
    JAVA的重载​在Java中,重载(Overloading)是指在同一个类中可以定义多个同名的方法,但它们的参数列表必须不同。重载可以通过改变参数的数量、类型或者顺序来实现。重载提高了代码的可读性和灵活性。JAVA重载要满足的条件:在同一个类下:java的重载必须在同一个类之下方法名相同......
  • 【Java基础】物理内存&虚拟内存
    前言在Java程序运行过程中,操作系统为其分配了物理内存和虚拟内存。理解这两者的概念有助于明晰内存管理和性能优化。一、物理内存物理内存是指计算机的实际RAM(随机存取存储器)。Java进程在运行时需要向操作系统请求内存资源,操作系统通过分配物理内存来满足Java进程的内存......
  • Stable Diffusion之提示词指南
    负提示词负向提示词:简单说就是告诉AI你想不要绘制什么,不要在画面中出现的内容。可以看到在WebUI页面中负提示词也是和正提示词一样,有一个输入框,一般我们不输入也是可以的。使用负面提示词是引导图像的另一种好方法,这里放的不是你想要的东西,而是你不想要的东西。它们不......
  • Python+PyCharm安装教程,Python+Pycharm环境配置和使用指南,以及如何使用pycharm运行你
    Python+PyCharm安装教程,Python+Pycharm环境配置和使用指南,以及如何使用pycharm运行你的python代码(保姆级)一、Python下载与安装真实在Windows上安装过Python的小伙伴会发现在Windows上安装Python真的和安装普通软件一样简单,我们只需要在官网下载个安装包,然后猛击“下一步......
  • Cloudflare Workers快速入门指南
    以下是一个快速入门指南,帮助您开始使用CloudflareWorkers:安装WranglerCLI首先,您需要安装WranglerCLI,这是Cloudflare官方提供的用于管理Workers的命令行工具。npminstall-gwrangler创建新的Worker项目使用Wrangler创建一个新的Worker项目:wranglerg......
  • 动态内存管理 (上)
    目录1.为什么要有动态内存分配 2.malloc和free2.1malloc2.11malloc申请空间和数组的空间有什么区别呢?2.2free3.calloc和realloc3.1calloc 3.2realloc 4.常⻅的动态内存的错误4.1对NULL指针的解引⽤操作4.2对动态开辟空间的越界访问 4.3对⾮动态开......
  • 指南针成本均线指标(CCMA)转换为通达信(TDX)的公式
    指南针成本均线指标(CCMA)转换为通达信(TDX)的公式,你可以使用通达信的公式语言(通达信公式语言类似于一种脚本语言,用于编写技术指标和策略)。以下是一个示例,展示了如何在通达信中实现CCMA指标。通达信公式语言中的CCMA指标//指南针成本均线指标(CCMA)//参数:N-计算周期,Alpha-......
  • JVM-直接内存(转)
    原文:https://cloud.tencent.com/developer/article/2357077作者:程序员朱永胜1.什么是JVM直接内存?JVM直接内存(DirectMemory)是JVM运行时使用的一种特殊内存区域,它是JVM堆外的一块内存空间。在Java中,我们使用java.nio包和java.lang.System类中的arraycopy()方法等来......
  • 【C语言】动态内存管理(上)
    本篇博客将讲解以下知识点:(1)为什么要有动态内存分配(2)malloc和free1、为什么要有动态内存分配我们已经掌握的内存开辟方式有:intval=40;//向内存中申请4个字节空间存储valchararr[10];//向内存申请10个字节空间 上述的开辟空间的方式有两个特点:(1)空间的开辟......
  • 《 C++ 修炼全景指南:十六 》玩转 C++ 特殊类:C++ 六种必备特殊类设计的全面解析
    摘要这篇博客深入探讨了六种C++特殊类的设计及其技术细节。首先,介绍了如何设计只能在堆上或栈上创建对象的类,通过控制构造函数的访问权限来限定对象的内存分配区域。接着,探讨了如何设计一个不能被拷贝的类,避免资源重复释放的问题。随后,介绍了如何防止类被继承以及单例模......