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

用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.mfbz.cn/a/758667.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

探秘 NTHU-DDD:疲劳与哈欠背后的驾驶安全密码(目标检测)

亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 一、引言…

【最新鸿蒙应用开发】——用户信息封装

用户管理工具封装 1. 为什么要封装 在进行如下登录功能时&#xff0c; 通常需要将一些用户信息以及token进行持久化保存&#xff0c;以方便下次进行数据请求时携带这些用户信息来进行访问后端数据。下面分享一下鸿蒙当中实用的持久化封装操作。 2. 步骤 封装用户信息管理工具…

Django 配置静态文件

1&#xff0c;DebugTrue 调试模式 Test/Test/settings.py DEBUG True...STATICFILES_DIRS [os.path.join(BASE_DIR, static),] STATIC_URL /static/ 1.1 创建静态文件 Test/static/6/images/Sni1.png 1.2 添加视图函数 Test/app6/views.py from django.shortcuts impor…

【D3.js in Action 3 精译】1.2.2 可缩放矢量图形(二)

当前内容所在位置 第一部分 D3.js 基础知识 第一章 D3.js 简介 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知 1.2.1 HTML 与 DOM1.2.2 SVG - 可缩放矢量图形 ✔️ 第一部分【第二部分】✔️第三部分&#xff08;精译中 ⏳&#xff09; 1.2.3 Canvas 与 WebGL&#x…

互联网大厂核心知识总结PDF资料

我们要敢于追求卓越&#xff0c;也能承认自己平庸&#xff0c;不要低估3&#xff0c;5&#xff0c;10年沉淀的威力 hi 大家好&#xff0c;我是大师兄&#xff0c;大厂工作特点是需要多方面的知识和技能。这种学习和积累一般人需要一段的时间&#xff0c;不太可能一蹴而就&…

获取 url 地址栏 ? 后面的查询字符串,并以键值对形式放到对象里面

写在前面 在前端面试当中&#xff0c;关于 url 相关的问题很常见&#xff0c;而对于 url 请求参数的问题也很常见&#xff0c;大部分以笔试题常见&#xff0c;今天就根据这道面试题一起来看一下。 问题 获取 url 地址栏?后面的查询字符串&#xff0c;并以键值对形式放到对象…

【图解大数据技术】Hadoop、HDFS、MapReduce、Yarn

【图解大数据技术】Hadoop、HDFS、MapReduce、Yarn HadoopHDFSHDFS架构写文件流程读文件流程 MapReduceMapReduce简介MapReduce整体流程 Yarn Hadoop Hadoop是Apache开源的分布式大数据存储与计算框架&#xff0c;由HDFS、MapReduce、Yarn三部分组成。广义上的Hadoop其实是指H…

Ubuntu网络管理命令:netstat

安装Ubuntu桌面系统&#xff08;虚拟机&#xff09;_虚拟机安装ubuntu桌面版-CSDN博客 顾名思义&#xff0c;netstat命令不是用来配置网络的&#xff0c;而是用来查看各种网络信息的&#xff0c;包括网络连接、路由表以及网络接口的各种统计数据等。 netstat命令的基本语法如…

Sora DiT图解【文生视频】

“在古老的迪萨罗斯大陆&#xff0c;曾经住着一位传奇人物&#xff0c;名叫索拉&#xff08;Sora&#xff09;。这个传奇人物体现了无限潜力的本质&#xff0c;包括天空的浩瀚和壮丽。 当它飞得很高&#xff0c;彩虹色的翅膀横跨广阔的空间&#xff0c;光线从它引人注目的身体…

QTreeView第一列自适应

通过setStretchLastSection(bool stretch)可以设置最后一列自适应,对于QTreeView,stretch默认为true。但有时候我们需要设置第一列自适应,比如文件浏览器,共有名称、大小和修改日期三列,大小和日期的宽度几乎是固定的,但名称却可长可短,此时我们希望在窗口大小变化时,第…

【算法专题--栈】用栈实现队列 -- 高频面试题(图文详解,小白一看就懂!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐双栈 模拟 队列 &#x1f95d;栈 和 队列 的特性 &#x1f34d;具体思路 &#x1f34d;案例图解 四、总结与提炼 五、共勉 一、前言 用栈实现队列 这道题&#xff0c;可以说是--栈专题--&#xff0c;最经典的一道题&…

Python数据分析-股票分析和可视化(深证指数)

一、内容简介 股市指数作为衡量股市整体表现的重要工具&#xff0c;不仅反映了市场的即时状态&#xff0c;也提供了经济健康状况的关键信号。在全球经济体系中&#xff0c;股市指数被广泛用于预测经济活动&#xff0c;评估投资环境&#xff0c;以及制定财政和货币政策。在中国…

【入门】5分钟了解卷积神经网络CNN是什么

本文来自《老饼讲解-BP神经网络》https://www.bbbdata.com/ 目录 一、卷积神经网络的结构1.1.卷积与池化的作用2.2.全连接层的作用 二、卷积神经网络的运算2.1.卷积层的运算2.2.池化的运算2.3.全连接层运算 三、pytorch实现一个CNN例子3.1.模型的搭建3.2.CNN完整训练代码 CNN神…

几种热管的构造

1、超薄热管构造形式 在实际应用中&#xff0c;超薄热管通常定义为厚度小于2.0mm的平板热管。超薄热管很薄&#xff0c;可紧贴电子元件表面散热&#xff0c;故被广泛应用于移动和可携带电子设备&#xff0c;如智能手机、笔记本电脑和智能手表。用于笔记本电脑和平板电脑的超薄…

【机器学习】Python中sklearn中数据基础处理与分析过程

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 1. 简介 ​编辑 1.1 什么是Scikit-learn 介绍Scikit-learn 应用领域 1.2 安装Scikit-learn 安装步骤 必要的依赖 2. 数据处理 2.1 创建示例数据 2.2 数据预处理 处理缺失值 特征编码 特征缩放 3. 数据…

设计者思维丨权限轴

应用背景 数据的本质是为了业务服务&#xff0c;从而达到更高效的工作方式&#xff0c;实现数据对业务的赋能和推动作用。 因此在构建报表时&#xff0c;需要开发者有设计思维&#xff0c;能够考虑多种应用场景&#xff0c;帮助业务解决实际应用中的问题。 例如&#xff0c;在实…

昇思MindSpore学习入门-函数式自动微分

函数式自动微分 神经网络的训练主要使用反向传播算法&#xff0c;模型预测值&#xff08;logits&#xff09;与正确标签&#xff08;label&#xff09;送入损失函数&#xff08;loss function&#xff09;获得loss&#xff0c;然后进行反向传播计算&#xff0c;求得梯度&#…

论文解读:【CVPR2024】DUSt3R: Geometric 3D Vision Made Easy

论文“”https://openaccess.thecvf.com/content/CVPR2024/papers/Wang_DUSt3R_Geometric_3D_Vision_Made_Easy_CVPR_2024_paper.pdf 代码&#xff1a;GitHub - naver/dust3r: DUSt3R: Geometric 3D Vision Made Easy DUSt3R是一种旨在简化几何3D视觉任务的新框架。作者着重于…

Java高级重点知识点-17-异常

文章目录 异常异常处理自定义异常 异常 指的是程序在执行过程中&#xff0c;出现的非正常的情况&#xff0c;最终会导致JVM的非正常停止。Java处 理异常的方式是中断处理。 异常体系 异常的根类是 java.lang.Throwable&#xff0c;&#xff0c;其下有两个子类&#xff1a;ja…