CUDA运行时 Runtime(一)            

一. 概述

运行时在cudart库中实现,该库通过静态方式链接到应用程序库cudart.lib和libcudart.a,或动态通过cudart.dll或者libcudart.so. 需要cudart.dll和/或libcudart。索对于动态链接,通常将它们作为应用程序安装包的一部分包括在内。只有在链接到CUDA运行时的同一实例的组件之间传递CUDA运行时符号的地址才是安全的。

它的所有入口点都以cuda为前缀。

正如在异构编程中所提到的,CUDA编程模型假设一个系统由一个主机和一个设备组成,每个主机和设备都有各自独立的内存。设备内存概述了用于管理设备内存的运行时函数。

共享内存说明了如何使用线程层次结构中引入的共享内存来最大限度地提高性能。

页面锁定主机内存引入了页面锁定主机内存,它需要将内核执行与主机和设备内存之间的数据传输重叠起来。

异步并发执行描述了用于在系统的各个级别上启用异步并发执行的概念和API。

多设备系统显示了编程模型如何扩展到多个设备连接到同一主机的系统。

错误检查描述如何正确检查运行时生成的错误。

调用堆栈提到用于管理CUDA C++调用堆栈的运行时函数。

纹理和表面存储器提供了纹理和表面存储器空间,它们提供了访问设备存储器的另一种方式;它们还公开了GPU纹理硬件的一个子集。

图形互操作性引入了运行时提供的与两个主要图形api(OpenGL和Direct3D)互操作的各种功能。

二.初始化

运行时没有显式的初始化函数;它在第一次调用运行时函数时初始化(更具体地说,除了参考手册的错误处理和版本管理部分中的函数以外的任何函数)。在计时运行时函数调用和将错误代码从第一次调用解释到运行时时,需要记住这一点。

在初始化期间,运行时为系统中的每个设备创建一个CUDA上下文(有关CUDA上下文的更多详细信息,请参阅上下文)。此上下文是此设备的主上下文,它在应用程序的所有主机线程之间共享。作为此上下文创建的一部分,如果需要,设备代码将及时编译(请参阅及时编译)并加载到设备内存中。这一切都是透明的。如果需要,例如对于驱动程序API互操作性,可以从驱动程序API访问设备的主上下文,如运行时API和驱动程序API互操作性中所述。

当主机线程调用cudaDeviceReset()时,这会破坏主机线程当前操作的设备(即设备选择中定义的当前设备)的主上下文。任何将此设备作为当前设备的主机线程进行的下一次运行时函数调用将为此设备创建新的主上下文。

注意:CUDA接口使用全局状态,全局状态在主机程序启动时初始化,在主机程序终止时销毁。CUDA运行时和驱动程序无法检测此状态是否无效,因此在程序启动或在主程序之后终止期间使用这些接口(隐式或显式)将导致未定义的行为。

三.设备存储器

正如在异构编程中所提到的,CUDA编程模型假设一个系统由一个主机和一个设备组成,每个主机和设备都有各自独立的内存。内核在设备内存中运行,因此运行时提供分配、取消分配和复制设备内存以及在主机内存和设备内存之间传输数据的功能。

设备存储器可以作为线性存储器或CUDA阵列分配。

CUDA阵列是为纹理提取优化的不透明内存布局。它们在纹理和表面记忆中被描述。 线性存储器被分配在一个统一的地址空间中,这意味着单独分配的实体可以通过指针相互引用,例如,在二叉树或链表中。地址空间的大小取决于主机系统(CPU)和所用GPU的计算能力:

注意:在具有计算能力5.3(Maxwell)和更早版本的设备上,CUDA驱动程序创建一个未提交的40位虚拟地址存储,以确保内存分配(指针)落入支持的范围。此存储显示为存储虚拟内存,但在程序实际分配内存之前不会占用任何物理内存。

线性内存通常使用cudaMalloc()分配,使用cudaFree()释放,主机内存和设备内存之间的数据传输通常使用cudaMemcpy()完成。在内核的矢量加法代码示例中,需要将矢量从主机内存复制到设备内存:

// Device code

__global__ void VecAdd(float* A, float* B, float* C, int N)

{

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

if (i < N) C[i] = A[i] + B[i];

}

// Host code

int main()

{

int N = ...;

size_t size = N * sizeof(float);

// Allocate input
vectors h_A and h_B in host memory

float* h_A = (float*)malloc(size);

float* h_B = (float*)malloc(size);

// Initialize input
vectors ... //
Allocate vectors in device memory

float* d_A;
cudaMalloc(&d_A, size);

float* d_B;
cudaMalloc(&d_B, size);

float* d_C;
cudaMalloc(&d_C, size); //
Copy vectors from host memory to device memory

cudaMemcpy(d_A, h_A, size,
cudaMemcpyHostToDevice);

cudaMemcpy(d_B, h_B, size,
cudaMemcpyHostToDevice); //
Invoke kernel

int threadsPerBlock = 256;

int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;

VecAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, N);

//
Copy result from device memory to host memory // h_C contains the result in host memory

cudaMemcpy(h_C, d_C, size,
cudaMemcpyDeviceToHost); //
Free device memory

cudaFree(d_A);

cudaFree(d_B);

cudaFree(d_C); //
Free host memory

...

}

线性内存也可以通过cudamalocpatch()和cudamaloc3d()分配。建议将这些函数用于2D或3D数组的分配,因为它确保适当地填充分配以满足设备内存访问中描述的对齐要求,从而确保在访问行地址或在2D数组和设备内存的其他区域之间执行复制时(使用cudammcpy2d()和cudammcpy3d()函数)。返回的力度(或步幅)必须用于访问数组元素。下面的代码示例分配一个宽x高的浮点值二维数组,并演示如何在设备代码中的数组元素上循环:

// Host code

int width = 64, height = 64;

float* devPtr; size_t pitch;

cudaMallocPitch(&devPtr, &pitch,
width * sizeof(float), height);

MyKernel<<<100, 512>>>(devPtr, pitch, width, height); //
Device code

__global__ void MyKernel(float* devPtr, size_t pitch, int width, int height)

{

for (int r = 0; r < height; ++r)

{

float* row = (float*)((char*)devPtr + r * pitch);

for (int c = 0; c < width; ++c)

{

float element = row[c];

}

}

}

下面的代码示例为浮点值分配一个宽度x高度x深度的三维数组,并演示如何在设备代码中的数组元素上循环:

// Host code

int width = 64, height = 64, depth = 64;

cudaExtent extent = make_cudaExtent(width * sizeof(float), height, depth);

cudaPitchedPtr devPitchedPtr;
cudaMalloc3D(&devPitchedPtr, extent);

MyKernel<<<100, 512>>>(devPitchedPtr, width, height, depth);

// Device
code

__global__ void MyKernel(cudaPitchedPtr devPitchedPtr, int width, int height, int depth)

{

char* devPtr = devPitchedPtr.ptr;

size_t pitch = devPitchedPtr.pitch;

size_t
slicePitch = pitch * height;

for (int z = 0; z < depth; ++z)

{

char* slice = devPtr + z * slicePitch;

for (int y = 0;y < height; ++y)

{

float* row =(float*)(slice+ y * pitch);

for (int x = 0; x < width; ++x)

{

float element = row[x];

}

}

}

}

参考手册列出了用于在使用cudaMalloc()分配的线性内存、使用cudamalocpitch()或cudamaloc3d()分配的线性内存、CUDA数组和为全局或恒定内存空间中声明的变量分配的内存之间复制内存的所有各种函数。

下面的代码示例演示了通过运行时API访问全局变量的各种方法:下面的代码示例分配了一个宽x高x深的浮点值三维数组,并演示了如何在设备代码中循环数组元素:

__constant__ float constData[256];

float data[256];

cudaMemcpyToSymbol(constData, data, sizeof(data));

cudaMemcpyFromSymbol(data, constData, sizeof(data));

__device__ float devData;

float value = 3.14f;

cudaMemcpyToSymbol(devData, &value, sizeof(float));

__device__ float* devPointer; float* ptr;

cudaMalloc(&ptr, 256 * sizeof(float));

cudaMemcpyToSymbol(devPointer, &ptr, sizeof(ptr));

cudaGetSymbolAddress()用于检索指向为全局内存空间中声明的变量分配的内存的地址。分配的内存大小是通过cudaGetSymbolSize()获得的。

四.共享内存

如变量内存空间说明符中所述,共享内存是使用共享内存空间说明符分配的。

共享内存预计将比线程层次结构中提到的和共享内存中详细描述的全局内存快得多。它可以用作scratchpad内存(或软件管理的缓存),以最小化来自CUDA块的全局内存访问,如下面的矩阵乘法示例所示。

下面的代码示例是不利用共享内存的矩阵乘法的直接实现。每个线程读取A的一行和B的一列,并计算C的相应元素,如图9所示。因此,A从全局内存中读取B.width times,B从A.height times中读取。

// Matrices are
stored in row-major order: // M(row, col) = *(M.elements + row *M.width + col)

typedef struct

{

int width;

int height;

float*elements;

} Matrix;

// Thread block size

#define BLOCK_SIZE 16

// Forward
declaration of the matrix multiplication kernel

__global__ void MatMulKernel(const Matrix, const Matrix, Matrix);

// Matrix
multiplication - Host code // Matrix dimensions are assumed to be
multiples of BLOCK_SIZE

void MatMul(const Matrix
A, const Matrix
B, Matrix C)

{

// Load A and B to
device memory

Matrix d_A;

d_A.width = A.width;

d_A.height = A.height;

size_t size = A.width * A.height * sizeof(float);

cudaMalloc(&d_A.elements, size);

cudaMemcpy(d_A.elements, A.elements, size,
cudaMemcpyHostToDevice);

Matrix d_B;

d_B.width = B.width;

d_B.height = B.height;

size = B.width * B.height * sizeof(float);

cudaMalloc(&d_B.elements, size);

cudaMemcpy(d_B.elements, B.elements, size,
cudaMemcpyHostToDevice);

// Allocate C in
device memory

Matrix d_C;

d_C.width = C.width;

d_C.height = C.height;

size = C.width * C.height * sizeof(float);

cudaMalloc(&d_C.elements, size);

// Invoke kernel

dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);

dim3 dimGrid(B.width / dimBlock.x, A.height / dimBlock.y);

MatMulKernel<<<dimGrid,
dimBlock>>>(d_A, d_B, d_C);

// Read C from
device memory

cudaMemcpy(C.elements, Cd.elements, size,
cudaMemcpyDeviceToHost);

// Free device memory

cudaFree(d_A.elements);

cudaFree(d_B.elements);

cudaFree(d_C.elements);

}

// Matrix
multiplication kernel called by MatMul()

__global__ void MatMulKernel(Matrix A, Matrix B, Matrix C)

{

// Each thread
computes one element of C // by accumulating results into Cvalue

float Cvalue= 0;

int row = blockIdx.y * blockDim.y + threadIdx.y;

int col = blockIdx.x * blockDim.x + threadIdx.x;

for (int e = 0; e < A.width; ++e)

Cvalue += A.elements[row * A.width + e] *
B.elements[e * B.width + col];

C.elements[row * C.width + col] = Cvalue;

}

图9. 无共享内存的矩阵乘法

CUDA运行时 Runtime(一)的更多相关文章

  1. CUDA运行时 Runtime(四)

    CUDA运行时 Runtime(四) 一.     图 图为CUDA中的工作提交提供了一种新的模型.图是一系列操作,如内核启动,由依赖项连接,依赖项与执行分开定义.这允许定义一次图形,然后重复启动.将 ...

  2. CUDA运行时 Runtime(三)

    CUDA运行时 Runtime(三) 一.异步并发执行 CUDA将以下操作公开为可以彼此并发操作的独立任务: 主机计算: 设备计算: 从主机到设备的内存传输: 从设备到主机的存储器传输: 在给定设备的 ...

  3. CUDA运行时 Runtime(二)

    CUDA运行时 Runtime(二) 一. 概述 下面的代码示例是利用共享内存的矩阵乘法的实现.在这个实现中,每个线程块负责计算C的一个方子矩阵C sub,块内的每个线程负责计算Csub的一个元素.如 ...

  4. Deep Learning部署TVM Golang运行时Runtime

    Deep Learning部署TVM Golang运行时Runtime 介绍 TVM是一个开放式深度学习编译器堆栈,用于编译从不同框架到CPU,GPU或专用加速器的各种深度学习模型.TVM支持来自Te ...

  5. iOS运行时Runtime浅析

    运行时是iOS中一个很重要的概念,iOS运行过程中都会被转化为runtime的C代码执行.例如[target doSomething];会被转化成objc)msgSend(target,@select ...

  6. “ compiler-rt”运行时runtime库

    " compiler-rt"运行时runtime库 编译器-rt项目包括: Builtins-一个简单的库,提供了代码生成和其他运行时runtime组件所需的特定于目标的低级接口. ...

  7. 【原】iOS动态性(五)一种可复用且解耦的用户统计实现(运行时Runtime)

    声明:本文是本人 编程小翁 原创,转载请注明. 为了达到更好的阅读效果,强烈建议跳转到这里查看文章. iOS动态性是我的关于iOS运行时的系列文章,由浅入深,从理论到实践.本文是第5篇.有兴趣可以看看 ...

  8. iOS 运行时runtime控制私有变量以及私有方法

    OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtime机制让我们可以在程序运行时动态修改类.对象中的所有属性.方法,就算是私有方法以及私有属性都是可以动 ...

  9. 【原】iOS动态性(二):运行时runtime初探(强制获取并修改私有变量,强制增加及修改私有方法等)

    OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtime机制让我们可以在程序运行时动态修改类.对象中的所有属性.方法,就算是私有方法以及私有属性都是可以动 ...

随机推荐

  1. hdu2492 数状数组或者线段树

    题意:      给你一些人,每个人有自己的攻击力,输入的顺序就是每个人的顺序,他们之间互相比赛,两个人比赛的条件是必须在他们两个位置之间找到一个人当裁判,这个裁判的攻击力必须在他们两个人之间,问你最 ...

  2. Windows XP sp3 系统安装 Windbg 符号文件 Symbols 时微软失去支持的解决方案

    0x01 前言 Windbg 是微软的正宗调试器,在正常的情况下调试一些程序并没有什么问题,但是如果需要调试分析程序的堆栈,或者是一些特殊的功能时则需要微软的符号文件的支持,所以符号文件是非常重要的, ...

  3. <JVM上篇:内存与垃圾回收篇>01-JVM与Java体系结构

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  4. MarkDown写ppt

    首先给你的VSCode安装插件 MarkDown语法 例子 --- marp: true paginate: true theme: default class: - lead - invert si ...

  5. JDBC往数据库里插入数据

    首先还是一个工具类 插入数据

  6. Lombok Requires Annotation Processing Annotation processing seems to be disabled for the project "HelloWorld". For  plugin to function correctly, please enable it under "Settings > Build > Compiler >

    更多精彩详见微信公众号  在网上查找说是插件的问题,但是我安装类插件父级项目没有开启注解处理Annotation Processor,子项目都有开启,如图,顶级项目是demo,下面的都是子项目,把第一 ...

  7. 如何解决 shell 脚本重复执行的问题

    在开发过程中,经常会使用shell脚本去完成定时备份的任务,普遍的做法是通过系统的定时任务定时执行备份脚本 设想这样一种场景,本次备份时间到了,自动执行备份脚本,如果备份比较耗时的话,会一直持续到下一 ...

  8. 合并两个yuv文件的C++代码

    //将BasketballPass_416x240_50.yuv序列的前50帧和BlowingBubbles_416x240_50.yuv序列的前250帧合并成out.yuv //参数配置416 24 ...

  9. BUAA-OO-第四单元总结——终章

    面向对象第四单元博客总结--终章 第四单元作业设计 第13次作业设计 类和对应方法属性设计 类设计如下图所示 本次作业主要涉及六个类,其中包括主类 Main ,通用Map类 UmlElementIdM ...

  10. [bug] @Test注解无法使用

    参考 https://blog.csdn.net/lixiangxiang666/article/details/83745901