用Roofline模型去分析pytorch和Triton算子

news2025/1/21 12:55:13

用Roofline模型去分析pytorch和Triton算子

  • 1.参考链接
  • 2.测试环境
  • 3.安装相关依赖
  • 4.锁频
  • 5.获取理论算力
  • 6.创建测试脚本
  • 7.运行测试程序生成Roofline图
  • 8.NVIDIA Nsight Compute生成Roofline
  • 9.效果图
    • A.nn.Linear
    • B.Triton实现

本文演示了如何用Roofline模型去分析pytorch和Triton算子
遗留问题:NVIDIA Nsight Compute中的Peak Work是怎么算出来的,又不是峰值算力 -> Nsight Compute 是怎么计算Roofline的呢

1.参考链接

  • roofline-overview
  • rtx-3060
  • OpenAI Triton

2.测试环境

+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.161.07             Driver Version: 535.161.07   CUDA Version: 12.2     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA GeForce RTX 3060        On  | 00000000:03:00.0 Off |                  N/A |
|  0%   48C    P5              29W / 170W |     18MiB / 12288MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+
torch==2.3.1+cu121

3.安装相关依赖

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64
export PATH=$PATH:/usr/local/cuda/bin
export CUDA_HOME=$CUDA_HOME:/usr/local/cuda
pip install pycuda

4.锁频

MAX_Graphics_CLOCK=`nvidia-smi -q -d SUPPORTED_CLOCKS | grep 'Graphics' | sed 's/[^0-9]//g' | sort -n | uniq | tail -n 1`
MAX_Memory_CLOCK=`nvidia-smi -q -d SUPPORTED_CLOCKS | grep 'Memory' | sed 's/[^0-9]//g' | sort -n | uniq | tail -n 1`
nvidia-smi -pm 1
nvidia-smi -lgc $MAX_Graphics_CLOCK,$MAX_Graphics_CLOCK
nvidia-smi -i 0 -ac $MAX_Memory_CLOCK,$MAX_Graphics_CLOCK
nvidia-smi -q -d CLOCK

5.获取理论算力

tee Theoretical_FLOPS.py <<-'EOF'
import pycuda.driver as cuda
import pycuda.autoinit

def get_gpu_compute_capability_and_clock_rate():
    device = cuda.Device(0)
    compute_capability = device.compute_capability()
    clock_rate = device.get_attribute(cuda.device_attribute.CLOCK_RATE)  # in kHz
    sm_count = device.get_attribute(cuda.device_attribute.MULTIPROCESSOR_COUNT)
    cores_per_sm = get_cuda_cores_per_sm(compute_capability)
    return compute_capability, clock_rate, sm_count, cores_per_sm

def get_cuda_cores_per_sm(compute_capability):
    major, minor = compute_capability
    if major == 2:
        return 32
    elif major == 3:
        return 192
    elif major == 5:
        return 128
    elif major == 6 and minor in [0, 1]:
        return 64
    elif major == 6 and minor == 2:
        return 128
    elif major == 7 and minor in [0, 5]:
        return 64
    elif major == 7 and minor == 2:
        return 64
    elif major == 8 and minor in [0, 6]:
        return 128
    else:
        raise ValueError("Unknown compute capability")

def calculate_theoretical_flops(clock_rate, sm_count, cores_per_sm):
    clock_rate_hz = clock_rate * 1e3  # Convert kHz to Hz
    flops = clock_rate_hz * sm_count * cores_per_sm * 2  # 2 FLOPs per clock per core (FMA)
    return flops

compute_capability, clock_rate, sm_count, cores_per_sm = get_gpu_compute_capability_and_clock_rate()
theoretical_flops = calculate_theoretical_flops(clock_rate, sm_count, cores_per_sm)
print(f"GPU compute capability: {compute_capability}")
print(f"Clock rate (kHz): {clock_rate}")
print(f"Number of SMs: {sm_count}")
print(f"Cores per SM: {cores_per_sm}")
print(f"Theoretical FLOPS for float32: {theoretical_flops / 1e12} TFLOPS")
EOF
python Theoretical_FLOPS.py

输出

GPU compute capability: (8, 6)
Clock rate (kHz): 1852000
Number of SMs: 28
Cores per SM: 128
Theoretical FLOPS for float32: 13.275136 TFLOPS

6.创建测试脚本

tee roofline_model.py <<-'EOF'
import sys
import torch
import torch.nn as nn
import triton
import triton.language as tl
import math
import torch
import torch.nn as nn
from fvcore.nn import FlopCountAnalysis, ActivationCountAnalysis
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties
import os
import argparse

# 定义一个测试模型
class SimpleModel(nn.Module):
    def __init__(self,input_features,output_features):
        super(SimpleModel, self).__init__()
        self.fc1 = nn.Linear(input_features,output_features,bias=False)
    def forward(self, x):
        x = self.fc1(x)
        return x

@triton.jit
def sgemm_kernel(
    A, B, C,
    M, N, K,
    stride_am, stride_ak,
    stride_bk, stride_bn,
    stride_cm, stride_cn,
    BLOCK_SIZE: tl.constexpr
):
    """ Kernel for computing C = A @ B """
    # Define the program ids
    pid_m = tl.program_id(0)
    pid_n = tl.program_id(1)

    # Create base pointers for A and B and C
    offs_am = pid_m * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
    offs_bn = pid_n * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
    offs_ak = tl.arange(0, BLOCK_SIZE)
    
    a_ptrs = A + (stride_am * offs_am[:, None] + stride_ak * offs_ak[None, :])
    b_ptrs = B + (stride_bk * offs_ak[:, None] + stride_bn * offs_bn[None, :])
    
    # Initialize accumulator
    acc = tl.zeros((BLOCK_SIZE, BLOCK_SIZE), dtype=tl.float32)
    
    # Loop over K dimension
    for k in range(0, K, BLOCK_SIZE):
        a = tl.load(a_ptrs, mask=offs_am[:, None] < M)
        b = tl.load(b_ptrs, mask=offs_bn[None, :] < N)
        acc += tl.dot(a, b)
        a_ptrs += BLOCK_SIZE * stride_ak
        b_ptrs += BLOCK_SIZE * stride_bk

    # Write back results
    c_ptrs = C + stride_cm * offs_am[:, None] + stride_cn * offs_bn[None, :]
    tl.store(c_ptrs, acc, mask=(offs_am[:, None] < M) & (offs_bn[None, :] < N))

class TritonLinear(nn.Module):
    def __init__(self, in_features, out_features):
        super(TritonLinear, self).__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.weight = nn.Parameter(torch.randn(out_features, in_features).float()).cuda()
    
    def forward(self, x):
        assert x.shape[1] == self.in_features
        out = torch.empty((x.shape[0], self.out_features), device=x.device, dtype=x.dtype).cuda()
        grid = lambda META: (math.ceil(x.shape[0] / META['BLOCK_SIZE']), math.ceil(self.out_features / META['BLOCK_SIZE']))
        sgemm_kernel[grid](
            x, self.weight, out,
            x.shape[0], self.out_features, self.in_features,
            x.stride(0), x.stride(1),
            self.weight.stride(0), self.weight.stride(1),
            out.stride(0), out.stride(1),
            BLOCK_SIZE=64
        )
        return out
        
def main(args):
    # 模型和输入数据

    input_features = 8192
    output_features = 8192
    batch_size = 8192

    model = SimpleModel(input_features,output_features)
    input_data = torch.randn(batch_size, input_features)    
    
    test_count=10
    # 计算 FLOPs 和内存访问量
    flops = FlopCountAnalysis(model, input_data).total()*test_count
    activations = ActivationCountAnalysis(model, input_data).total() + input_data.numel()
    print("activations:",activations)
    # 计算参数个数
    params = sum(p.numel() for p in model.parameters())

    # 内存访问量假定为 activations 和params 乘以 4 字节(假设 activations 和 params 是 float32 类型)
    activation_memory_access = activations * 4
    params_memory_access = params * 4
    memory_access = activation_memory_access + params_memory_access
    memory_access=memory_access*test_count
    
    if args.triton_kernel:
        model = TritonLinear(in_features=input_features, out_features=output_features)
    else:
        model=model.cuda()
        
    input_data=input_data.float().cuda()
    
    for i in range(5):
        output = model(input_data) 
        torch.cuda.synchronize()
    
    if args.warmup_only:
        return

    if False:
        # 设置 CUDA 事件用于计算执行时间
        start_event = torch.cuda.Event(enable_timing=True)
        end_event = torch.cuda.Event(enable_timing=True)
        start_event.record()
        for _ in range(test_count):
            output = model(input_data)
        end_event.record()
        torch.cuda.synchronize()
        total_cuda_time = start_event.elapsed_time(end_event) / 1000  # 转换为秒
    else:
        # 使用 PyTorch Profiler 计算 FLOPs、内存访问和执行时间
        with torch.profiler.profile(
            activities=[torch.profiler.ProfilerActivity.CUDA]) as prof:
            for _ in range(test_count):
                output = model(input_data)
        key_averages = prof.key_averages()
        for ev in key_averages:
            print(ev)
        total_cuda_time = sum([event.self_cuda_time_total for event in key_averages if event.key.find("sgemm")>=0]) / 1e6  # 转换至秒

    # FLOPs 转换至 GFLOPs
    flops_measured_glops = flops / 1e9

    # 内存带宽测量
    memory_access_gb=memory_access/ 1e9
    bandwidth_measured = memory_access_gb / total_cuda_time  # 单位:GB/s
    print("bandwidth_measured:",bandwidth_measured)

    # GPU 的峰值性能和带宽
    peak_performance = 13.275136  * 1e3  # 单位:GFLOPs
    memory_bandwidth = 360.0  # 单位:GB/s

    # 计算 Roofline 模型中的数据点
    Io = np.logspace(-2,4,100) #GFLOPs/GB
    performance = np.minimum(peak_performance, Io * memory_bandwidth)  #不同计算密度下的最大FLOPs/S,上限为峰值算力peak_performance

    # 绘制 Roofline 模型
    plt.figure(figsize=(10, 6))

    thresold=0.75

    # 设置字体以支持中文
    font_path = 'simsun.ttc'  # 在这里替换为你的字体路径
    font_prop = FontProperties(fname=font_path)
    
    # Bandwidth Bound
    x=Io[Io<(peak_performance / memory_bandwidth)]
    plt.fill_between(x, np.minimum(peak_performance, x * memory_bandwidth)*thresold,
                np.minimum(peak_performance, x * memory_bandwidth),
                color='lightblue', alpha=0.6, label='Bandwidth Bound')

    # Compute Bound
    x2=Io[Io>=(peak_performance / memory_bandwidth)]
    plt.fill_between(x2, np.minimum(peak_performance, x2 * memory_bandwidth)*thresold, 
                 np.minimum(peak_performance, x2 * memory_bandwidth), 
                 color='green', alpha=0.6, label='Compute Bound')

    # 绘制低性能区域
    plt.fill_between(Io, 0, np.minimum(peak_performance, Io * memory_bandwidth)*thresold,
                 color='gray', alpha=0.6, label='poor performance')

    plt.axhline(y=peak_performance, color='b', linestyle='--', 
            label=f'峰值计算能力:{peak_performance/1e3:.2f}TFLOPs')
            
    plt.axvline(x=peak_performance / memory_bandwidth, color='g', linestyle='--', 
            label=f'{peak_performance / memory_bandwidth:.2f}GFLOPs/GB')

    plt.loglog(Io, performance, label='Roofline')

    arithmetic_intensity_measured=flops_measured_glops/memory_access_gb #GFLOPs/GB(算法的静态属性)
    point_y = arithmetic_intensity_measured*bandwidth_measured

    plt.scatter(arithmetic_intensity_measured, point_y, c='r',
            label=f'Measured Points {point_y/1e3:.2f} TFLOPs/sec {point_y*100/peak_performance:.2f}%')

    plt.xlabel('操作强度 [GFLOPs/GB]', fontproperties=font_prop)
    plt.ylabel('性能 [GFLOPs/sec]', fontproperties=font_prop)
    plt.title('Roofline 模型', fontproperties=font_prop)
    plt.legend(prop=font_prop)

    # 保存图片而不显示
    plt.savefig('roofline_model.png')
    plt.close()

    print(f"FLOPs: {flops} FLOPs")
    print(f"内存访问量: {memory_access} 字节")
    print(f"执行时间: {total_cuda_time:.4f} 秒")
    print(f"理论值的:{point_y*100/peak_performance:.2f}%")


parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument("--warmup_only", action="store_true", help="warmup_only")
parser.add_argument("--triton_kernel", action="store_true", help="triton_kernel")

args = parser.parse_args()
main(args)
EOF

7.运行测试程序生成Roofline图

python roofline_model.py
python roofline_model.py --triton_kernel

输出

FLOPs: 5497558138880 FLOPs
内存访问量: 8053063680 字节
执行时间: 1.3862 秒
理论值的:29.87%

FLOPs: 5497558138880 FLOPs
内存访问量: 8053063680 字节
执行时间: 1.0957 秒
理论值的:37.80%

8.NVIDIA Nsight Compute生成Roofline

/usr/local/cuda/bin/ncu -f --section SpeedOfLight_HierarchicalSingleRooflineChart \
        --section ComputeWorkloadAnalysis --section MemoryWorkloadAnalysis \
        --target-processes all --export roofline_report python roofline_model.py --warmup_only


/usr/local/cuda/bin/ncu -f --section SpeedOfLight_HierarchicalSingleRooflineChart \
        --section ComputeWorkloadAnalysis --section MemoryWorkloadAnalysis \
        --target-processes all --export roofline_triton_kernel_report python roofline_model.py --warmup_only --triton_kernel

9.效果图

A.nn.Linear

在这里插入图片描述
在这里插入图片描述

B.Triton实现

请添加图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1880477.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mx Admin 基于react18的后台管理系统

前言 Mx Admin 基于React18 vite5 antd5的后台管理系统&#xff0c; 基于RBAC的权限控制系统&#xff0c;动态菜单和动态路由支持tab路由缓存嵌套菜单支持多种菜单布局模式亮暗色主题切换

AttGAN实验复现 2024

AttnGAN 代码复现 2024 文章目录 AttnGAN 代码复现 2024简介环境python 依赖数据集TrainingPre-train DAMSMTrain AttnGAN SamplingB_VALIDATION 为 False (默认)B_VALIDATION 为 True 参考博客 简介 论文地址&#xff1a; https://arxiv.org/pdf/1711.10485.pdf 代码 python…

Unity实现简单的MVC架构

文章目录 前言MVC基本概念示例流程图效果预览后话 前言 在Unity中&#xff0c;MVC&#xff08;Model-View-Controller&#xff09;框架是一种架构模式&#xff0c;用于分离游戏的逻辑、数据和用户界面。MVC模式可以帮助开发者更好地管理代码结构&#xff0c;提高代码的可维护性…

【web】2、集成插件

1、element-plus 官网地址:设计 | Element Plus 安装 plus 及 icon 图标库 1.1 官网提供plus安装方法&#xff1a; 1.2 官网提供 icon 安装方法 1.3 安装 pnpm install element-plus element-plus/icons-vue main.ts全局安装element-plus,element-plus默认支持语言英语设…

isspace()方法——判断字符串是否只由空格组成

自学python如何成为大佬(目录): https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 isspace()方法用于判断字符串是否只由空格组成。isspace()方法的语法格式如下&#xff1a; str.isspace() 如果字符串中只包含空格&…

深度学习基准模型Mamba

深度学习基准模型Mamba Mamba(英文直译&#xff1a;眼镜蛇)具有选择性状态空间的线性时间序列建模&#xff0c;是一种先进的状态空间模型 (SSM)&#xff0c;专为高效处理复杂的数据密集型序列而设计。 Mamba是一种深度学习基准模型&#xff0c;专为处理长序列数据而设计&…

ONLYOFFICE 8.1 版本桌面编辑器测评

在现代办公环境中&#xff0c;办公软件的重要性不言而喻。从文档处理到电子表格分析&#xff0c;再到演示文稿制作&#xff0c;强大且高效的办公软件工具能够极大提升工作效率。ONLYOFFICE 作为一个功能全面且开源的办公软件套件&#xff0c;一直以来都受到广大用户的关注与喜爱…

C++:typeid4种cast转换

typeid typeid typeid是C标准库中提供的一种运算符&#xff0c;它用于获取类型的信息。它主要用于类型检查和动态类型识别。当你对一个变量或对象使用typeid运算符时&#xff0c;它会返回一个指向std::type_info类型的指针&#xff0c;这个信息包含了关于该类型名称、大小、基…

C#进阶-ASP.NET WebForms调用ASMX的WebService接口

ASMX 文件在 ASP.NET WebForms 中提供了创建 Web 服务的便捷方式&#xff0c;通过公开 Web 方法&#xff0c;允许远程客户端调用这些方法并获取数据。本文介绍了 ASMX 文件的基本功能、如何定义 WebService 接口、通过 HTTP 和 SOAP 请求调用 WebService 接口&#xff0c;以及使…

python实现网页自动化(自动登录需要验证的网页)

引言: python作为实现网页自动化的一个重要工具,其强大的各种封装的库使得程序运行更加简洁,只需要下载相应的库,然后调用库中的函数就可以简便的实现我们想要的网页相关操作。 正文: 我的前几篇文章写了关于初学爬虫中比较容易上手的功能,例如爬取静态网页的数据、动…

系统运维面试总结(shell编程)

SYNDDOS攻击&#xff0c;需要判断这个访问是正常访问还是信包攻击&#xff0c;当前这个信包发起的访问数量是多少&#xff0c;例如看到30个信包同时再访问时设置监控报警。

Wails 安装初体验

文章目录 Wails 安装说明1. 系统要求2. 安装步骤3. 构建应用 结论 Wails 安装说明 Wails 是一个用于构建桌面应用的 Go 框架&#xff0c;结合了现代前端技术。以下是安装步骤&#xff1a; 1. 系统要求 Go 1.16 或更高版本Node.js 和 npm可选&#xff1a;适用于 Windows、mac…

SSH版本升级-openssh-9.7p1

SSH版本升级-openssh-9.7p1 1、查看当前版本2、安装openssl2.1、编译安装ssl 3、下载新版本SSH4、备份原有的SSH配置5、上传文件并解压6、卸载原有的openssh包7、编译安装openssh7.1、在解压后的目录&#xff0c;初始化openssh7.2、将文件拷回7.3、修改配置文件 最终实现&#…

傻瓜交换机多网段互通组网、设备无法配置网关案例

记录一下&#xff1a; 一、傻瓜交换机多网段互通组网 1、客户在核心交换机上创建了VLAN10&#xff0c;VLAN20。 VLAN10&#xff1a;IP192.168.10.254 VLAN20&#xff1a;IP192.168.20.254 在核心交换机下挂了一台傻瓜交换机&#xff0c;傻瓜交换机接入了一台OA服务器IP&#…

Qt之Pdb生成及Dump崩溃文件生成与调试(含注释和源码)

文章目录 一、Pdb生成及Dump文件使用示例图1.Pdb文件生成2.Dump文件调试3.参数不全Pdb生成的Dump文件调试 二、个人理解1.生成Pdb文件的方式2.Dump文件不生产的情况 三、源码Pro文件mian.cppMainWindowUi文件 总结 一、Pdb生成及Dump文件使用示例图 1.Pdb文件生成 下图先通过…

Transformer详解encoder

目录 1. Input Embedding 2. Positional Encoding 3. Multi-Head Attention 4. Add & Norm 5. Feedforward Add & Norm 6.代码展示 &#xff08;1&#xff09;layer_norm &#xff08;2&#xff09;encoder_layer1 最近刚好梳理了下transformer&#xff0c;今…

深入理解PHP命名空间

在PHP项目中&#xff0c;命名空间&#xff08;namespace&#xff09;是一个非常重要的特性。它不仅帮助开发者组织代码&#xff0c;还能避免类、函数、常量等命名冲突问题。本文将详细介绍PHP命名空间的概念、使用方法和最佳实践。 一、什么是命名空间&#xff1f; 命名空间…

LeetCode:经典题之2、445 题解及延伸

系列目录 88.合并两个有序数组 52.螺旋数组 567.字符串的排列 643.子数组最大平均数 150.逆波兰表达式 61.旋转链表 160.相交链表 83.删除排序链表中的重复元素 389.找不同 1491.去掉最低工资和最高工资后的工资平均值 896.单调序列 206.反转链表 92.反转链表II 141.环形链表 …

github主页这样优化,让人眼前一亮

我的主页&#xff08;一之十六&#xff09; 1. 创建与账户ID同名的仓库 注意&#xff1a;记得勾选Add a README file 2. markdown语法自定义README.md 3. 辅助工具 优秀profile&#xff1a;https://zzetao.github.io/awesome-github-profile/动态文字&#xff1a;https://r…

pytest测试框架pytest-cov插件生成代码覆盖率

Pytest提供了丰富的插件来扩展其功能&#xff0c;本章介绍下pytest-cov插件&#xff0c;用于生成测试覆盖率报告&#xff0c;帮助开发者了解哪些部分的代码被测试覆盖&#xff0c;哪些部分还需要进一步的测试。 pytest-cov 支持多种报告格式&#xff0c;包括纯文本、HTML、XML …