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. Xposed学习一:初探

    学习Xposed框架,在github:https://github.com/rovo89 下载XposedInstaller安装到手机上来管理Xposed的模块. 本文记录根据官方文档(资料1)在an ...

  2. POJ2709 染料贪心

    题意:       要搭配出来n种颜料,每种颜料要用mi升,除了这n种颜色还有一个合成灰色的毫升数,灰色是由三种不同的颜色合成的,三种m m m 的不同颜色能合成m升灰色,然后问你满足要求至少要多少盒 ...

  3. HTTP自定义Header-(SOCKET-TCP)

      HTTP自定义Header-TCP 前几天弄一些东西,需要在发送http请求的时候自定义http头,找了几个库用着很不爽.有的把Cookie直接干掉了,还自己在头里加了版权,最后终于忍不了了.在网 ...

  4. 【python】Leetcode每日一题-森林中的兔子

    [python]Leetcode每日一题-森林中的兔子 [题目描述] 森林中,每个兔子都有颜色.其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色.我们将这些回答放在 answers ...

  5. Github镜像网站

    https://hub.fastgit.org

  6. 网络请求axios

    axios的定义 axios是一个基于Promise,用于浏览器和node的HTTP客户端 axios的功能特点 在浏览器中发送 XMLHttpRsquests 请求 在node.js中发送http请 ...

  7. LINQ之查询语法

    新开一节LINQ的入门讲解. LINQ(Language Integrated Query)语言集成查询,是C#语言的扩展,它的主要功能是从数据集中查询数据,就像通过sql语句从数据库查询数据一样(本 ...

  8. 【Azure Redis 缓存】Azure Cache for Redis服务中,除开放端口6379,6380外,对13000,13001,15000,15001 为什么也是开放的呢?

    问题描述 在使用安全检测工具对Azure Redis服务端口进行扫描时,发现Redis对外开放了13001, 13000,15000,15001端口.非常不理解的是,在门户上只开放了6379,6380 ...

  9. 技术博客:Azure 认知服务

    Azure 认知服务 1.概述 ​ 微软认知服务(Microsoft Cognitive Services)集合了多种智能API以及知识API,使每个开发人员无需具备机器学习的专业知识就能接触到 AI ...

  10. Spring 实现策略模式--自定义注解方式解耦if...else

    策略模式 定义 定义一簇算法类,将每个算法分别封装起来,让他们可以互相替换,策略模式可以使算法的变化独立于使用它们的客户端 场景 使用策略模式,可以避免冗长的if-else 或 switch分支判断 ...