国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

PyTorch如何實現自定義CUDA算子并調用的方法且測量CUDA程序耗時

深度學習自然語言處理 ? 來源:算法碼上來 ? 作者:算法碼上來 ? 2021-03-30 15:58 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

最近因為工作需要,學習了一波CUDA。這里簡單記錄一下PyTorch自定義CUDA算子的方法,寫了一個非常簡單的example,再介紹一下正確的PyTorch中CUDA運行時間分析方法。

完整流程

下面我們就來詳細了解一下PyTorch是如何調用自定義的CUDA算子的。

首先我們可以看到有四個代碼文件:

main.py,這是python入口,也就是你平時寫模型的地方。

add2.cpp,這是torch和CUDA連接的地方,將CUDA程序封裝成了python可以調用的庫。

add2.h,CUDA函數聲明。

add2.cu,CUDA函數實現。

然后逐個文件看一下是怎么調用的。

CUDA算子實現

首先最簡單的當屬add2.h和add2.cu,這就是普通的CUDA實現。

void launch_add2(float *c,

const float *a,

const float *b,

int n);

__global__ void add2_kernel(float* c,

const float* a,

const float* b,

int n) {

for (int i = blockIdx.x * blockDim.x + threadIdx.x;

i 《 n; i += gridDim.x * blockDim.x) {

c[i] = a[i] + b[i];

}

}

void launch_add2(float* c,

const float* a,

const float* b,

int n) {

dim3 grid((n + 1023) / 1024);

dim3 block(1024);

add2_kernel《《《grid, block》》》(c, a, b, n);

}

這里實現的功能是兩個長度為的tensor相加,每個block有1024個線程,一共有個block。具體CUDA細節就不講了,本文重點不在于這個。

add2_kernel是kernel函數,運行在GPU端的。而launch_add2是CPU端的執行函數,調用kernel。注意它是異步的,調用完之后控制權立刻返回給CPU,所以之后計算時間的時候要格外小心,很容易只統計到調用的時間。

Torch C++封裝

這里涉及到的是add2.cpp,這個文件主要功能是提供一個PyTorch可以調用的接口。

#include 《torch/extension.h》

#include “add2.h”

void torch_launch_add2(torch::Tensor &c,

const torch::Tensor &a,

const torch::Tensor &b,

int n) {

launch_add2((float *)c.data_ptr(),

(const float *)a.data_ptr(),

(const float *)b.data_ptr(),

n);

}

PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {

m.def(“torch_launch_add2”,

&torch_launch_add2,

“add2 kernel warpper”);

}

torch_launch_add2函數傳入的是C++版本的torch tensor,然后轉換成C++指針數組,調用CUDA函數launch_add2來執行核函數。

這里用pybind11來對torch_launch_add2函數進行封裝,然后用cmake編譯就可以產生python可以調用的.so庫。但是我們這里不直接手動cmake編譯,具體方法看下面的章節。

Python調用

最后就是python層面,也就是我們用戶編寫代碼去調用上面生成的庫了。

import time

import numpy as np

import torch

from torch.utils.cpp_extension import load

cuda_module = load(name=“add2”,

sources=[“add2.cpp”, “add2.cu”],

verbose=True)

# c = a + b (shape: [n])

n = 1024 * 1024

a = torch.rand(n, device=“cuda:0”)

b = torch.rand(n, device=“cuda:0”)

cuda_c = torch.rand(n, device=“cuda:0”)

ntest = 10

def show_time(func):

times = list()

res = list()

# GPU warm up

for _ in range(10):

func()

for _ in range(ntest):

# sync the threads to get accurate cuda running time

torch.cuda.synchronize(device=“cuda:0”)

start_time = time.time()

r = func()

torch.cuda.synchronize(device=“cuda:0”)

end_time = time.time()

times.append((end_time-start_time)*1e6)

res.append(r)

return times, res

def run_cuda():

cuda_module.torch_launch_add2(cuda_c, a, b, n)

return cuda_c

def run_torch():

# return None to avoid intermediate GPU memory application

# for accurate time statistics

a + b

return None

print(“Running cuda.。。”)

cuda_time, _ = show_time(run_cuda)

print(“Cuda time: {:.3f}us”.format(np.mean(cuda_time)))

print(“Running torch.。?!保?/p>

torch_time, _ = show_time(run_torch)

print(“Torch time: {:.3f}us”.format(np.mean(torch_time)))

這里6-8行的torch.utils.cpp_extension.load函數就是用來自動編譯上面的幾個cpp和cu文件的。最主要的就是sources參數,指定了需要編譯的文件列表。然后就可以通過cuda_module.torch_launch_add2,也就是我們封裝好的接口來進行調用。

接下來的代碼就隨心所欲了,這里簡單寫了一個測量運行時間,對比和torch速度的代碼,這部分留著下一章節講解。

總結一下,主要分為三個模塊:

先編寫CUDA算子和對應的調用函數。

然后編寫torch cpp函數建立PyTorch和CUDA之間的聯系,用pybind11封裝。

最后用PyTorch的cpp擴展庫進行編譯和調用。

運行時間分析

我們知道,CUDA kernel函數是異步的,所以不能直接在CUDA函數兩端加上time.time()測試時間,這樣測出來的只是調用CUDA api的時間,不包括GPU端運行的時間。

所以我們要加上線程同步函數,等待kernel中所有線程全部執行完畢再執行CPU端后續指令。這里我們將同步指令加在了python端,用的是torch.cuda.synchronize函數。

具體來說就是形如下面代碼:

torch.cuda.synchronize()

start_time = time.time()

func()

torch.cuda.synchronize()

end_time = time.time()

其中第一次同步是為了防止前面的代碼中有未同步還在GPU端運行的指令,第二次同步就是為了等fun()所有線程執行完畢后再統計時間。

這里我們torch和cuda分別執行10次看看平均時間,此外執行前需要先執行10次做一下warm up,讓GPU達到正常狀態。

我們分別測試四種情況,分別是:

兩次同步

第一次同步,第二次不同步

第一次不同步,第二次同步

兩次不同步

這里我們采用英偉達的Nsight Systems來可視化運行的每個時刻指令執行的情況。

安裝命令為:

sudo apt install nsight-systems

然后在運行python代碼時,在命令前面加上nsys profile就行了:

nsys profile python3 main.py

然后就會生成report1.qdstrm和report1.sqlite兩個文件,將report1.qdstrm轉換為report1.qdrep文件:

QdstrmImporter -i report1.qdstrm

最后將生成的report1.qdrep文件用Nsight Systems軟件打開,我這里是mac系統。

兩次同步

這是正確的統計時間的方法,我們打開Nsight Systems,放大kernel運行那一段可以看到下圖:

0256c144-8e8f-11eb-8b86-12bb97331649.png

其中第1和第3個框分別是cuda和torch的GPU warm up過程,這部分沒有進行線程同步(上面的黃色塊)。

而第2和第4個框就分別是cuda和torch的加法執行過程了,我們可以放大來看看。

02cd92ec-8e8f-11eb-8b86-12bb97331649.png

可以看出,每執行一次(一個框)都經過了三個步驟:先是調用api(左上角藍色框),然后執行kernel(下方藍色框),最后線程同步(右上角黃色框)。

所以最后算出來的時間就是這三個步驟的耗時,也就是下圖選中的范圍:

032b61ce-8e8f-11eb-8b86-12bb97331649.png

時間大概在29us左右,和我們實際代碼測出來的也是比較接近的:

039a9be8-8e8f-11eb-8b86-12bb97331649.png

其實我們實際想要知道的耗時并不包括api調用和線程同步的時間,但是這部分時間在python端不好去掉,所以就加上了。

第一次同步,第二次不同步

放大每次執行的過程:

可以看出,雖然長的和上一種情況幾乎一模一樣,但是在api調用完之后,立刻就進行計時了,所以耗時只有8us左右,實際測出來情況也是這樣的:

047e113e-8e8f-11eb-8b86-12bb97331649.png

第一次不同步,第二次同步

我們先來看一下實際統計的時間:

04eba01e-8e8f-11eb-8b86-12bb97331649.png

很奇怪是不是,第一次運行耗時非常久,那我們可視化看看到底怎么回事:

055a53ec-8e8f-11eb-8b86-12bb97331649.png

可以看出,因為第一次開始計時前沒有同步線程,所以在GPU warm up調用api完畢后,第一次cuda kernel調用就開始了。然后一直等到warm up執行完畢,才開始執行第一次cuda kernel,然后是線程同步,結束后才結束計時。這個過程非常長,差不多有130us左右。然后第二次開始執行就很正常了,因為kernel結束的同步相當于是下一次執行之前的同步。

兩次不同步

先來看看執行情況:

05ef66a8-8e8f-11eb-8b86-12bb97331649.png

可以看出因為沒有任何同步,所有GPU warm up和cuda kernel的api調用全接在一起了,執行也是。所以計時只計算到了每個api調用的時間,差不多在7us左右。

上面四種情況,torch指令情形幾乎一樣,因此不再贅述。

小結

通過這篇文章,應該可以大致了解PyTorch實現自定義CUDA算子并調用的方法,也能知道怎么正確的測量CUDA程序的耗時。

當然還有一些內容留作今后講解,比如如何實現PyTorch神經網絡的自定義前向和反向傳播CUDA算子、如何用TensorFlow調用CUDA算子等等。
編輯:lyn

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • python
    +關注

    關注

    57

    文章

    4876

    瀏覽量

    90025
  • CUDA
    +關注

    關注

    0

    文章

    127

    瀏覽量

    14475
  • pytorch
    +關注

    關注

    2

    文章

    813

    瀏覽量

    14850

原文標題:【進階】PyTorch自定義CUDA算子教程與運行時間分析

文章出處:【微信號:zenRRan,微信公眾號:深度學習自然語言處理】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    借助NVIDIA CUDA Tile IR后端推進OpenAI Triton的GPU編程

    NVIDIA CUDA Tile 是基于 GPU 的編程模型,其設計目標是為 NVIDIA Tensor Cores 提供可移植性,從而釋放 GPU 的極限性能。CUDA Tile 的一大優勢是允許開發者基于其構建自定義的 DS
    的頭像 發表于 02-10 10:31 ?240次閱讀

    如何在NVIDIA CUDA Tile中編寫高性能矩陣乘法

    本博文是系列課程的一部分,旨在幫助開發者學習 NVIDIA CUDA Tile 編程,掌握構建高性能 GPU 內核的方法,并以矩陣乘法作為核心示例。
    的頭像 發表于 01-22 16:43 ?4817次閱讀
    如何在NVIDIA <b class='flag-5'>CUDA</b> Tile中編寫高性能矩陣乘法

    如何在TensorFlow Lite Micro中添加自定義操作符(2)

    reshape算子進行說明,如何將reshape算子注冊到解析器中,接下來介紹如果我們想自定義一個算子需要干些什么。
    的頭像 發表于 12-26 10:53 ?1120次閱讀

    NVIDIA CUDA Tile的創新之處、工作原理以及使用方法

    NVIDIA CUDA 13.1 推出 NVIDIA CUDA Tile,這是自 2006 年 NVIDIA CUDA 平臺發明以來,最大的一次技術進步。這一令人振奮的創新引入了一套面向
    的頭像 發表于 12-24 10:17 ?462次閱讀
    NVIDIA <b class='flag-5'>CUDA</b> Tile的創新之處、工作原理以及使用<b class='flag-5'>方法</b>

    NVIDIA CUDA 13.1版本的新增功能與改進

    NVIDIA CUDA 13.1 是自 CUDA 二十年前發明以來,規模最大、內容最全面的一次更新。
    的頭像 發表于 12-13 10:08 ?2205次閱讀

    無圖形界面模式下自定義檢查工具的應用

    此前文章已介紹 ANSA 中的自定義檢查工具。本文將探討該功能在無圖形界面(No-GUI)模式下的應用,旨在滿足標準化工作流程的需求,適用于需要高度自動化的前處理場景。通過集成自定義檢查,用戶可實現工作流程的高效自動化運行。
    的頭像 發表于 11-30 14:13 ?581次閱讀
    無圖形界面模式下<b class='flag-5'>自定義</b>檢查工具的應用

    軟硬件協同技術分享 - 任務劃分 + 自定義指令集

    SoC自帶NICE協處理器接口,支持傳輸自定義指令。本設計在軟件層面利用C語言內聯函數的方式實現了6條自定義函數的定義。 軟件上傳輸參
    發表于 10-28 08:03

    采用匯編指示符來使用自定義指令

    具體實現 1、采用.word .half .dword等匯編指示符直接插入自定義指令,這種方法需要自己指定寄存器。其中.word為插入一個字的數據即32位,.half為插入半字即16位
    發表于 10-28 06:02

    進迭時空同構融合RISC-V AI CPU的Triton算子編譯器實踐

    Pytorch已能做到100%替換CUDA,國內也有智源研究院主導的FlagGems通用算子庫試圖構建起不依賴CUDA的AI計算生態,截至今日,FlagGems已進入Pyto
    的頭像 發表于 07-15 09:04 ?1840次閱讀
    進迭時空同構融合RISC-V AI CPU的Triton<b class='flag-5'>算子</b>編譯器實踐

    大彩講堂:VisualTFT軟件如何自定義圓形進度條

    VisualTFT軟件如何自定義圓形進度條
    的頭像 發表于 07-07 17:10 ?1637次閱讀
    大彩講堂:VisualTFT軟件如何<b class='flag-5'>自定義</b>圓形進度條

    KiCad 中的自定義規則(KiCon 演講)

    “ ?Seth Hillbrand 在 KiCon US 2025 上為大家介紹了 KiCad 的規則系統,詳細講解了自定義規則的設計與實例。? ” ? 演講主要圍繞 加強 KiCad 中的自定義
    的頭像 發表于 06-16 11:17 ?2129次閱讀
    KiCad 中的<b class='flag-5'>自定義</b>規則(KiCon 演講)

    HarmonyOS應用自定義鍵盤解決方案

    增強用戶輸入的安全性,避免敏感信息被截取或者泄露。本文介紹了自定義鍵盤的實現,結合自定義鍵盤和系統鍵盤的切換、自定義鍵盤的布局避讓等場景,
    的頭像 發表于 06-05 14:19 ?2396次閱讀

    如何使用自定義設置回調函數?

    你好,我正在嘗試編寫自己的自定義設置回調函數,使用 fastEnum=false。 是否有任何代碼示例或資料可供我參考? void CyU3PUsbRegisterSetupCallback
    發表于 05-21 06:11

    LabVIEW運動控制(三):EtherCAT運動控制器的高效加工指令自定義封裝

    LabVIEW高效加工指令自定義封裝
    的頭像 發表于 04-08 13:49 ?3628次閱讀
    LabVIEW運動控制(三):EtherCAT運動控制器的高效加工指令<b class='flag-5'>自定義</b>封裝

    如何添加自定義單板

    在開發過程中,用戶有時需要創建自定義板配置。本節將通過一個實例講解用戶如何創建屬于自己的machine,下面以g2l-test.conf為例進行說明。
    的頭像 發表于 03-12 14:43 ?1350次閱讀