相关:

CPU端多进程/多线程调用CUDA是否可以加速???

参考:

《CUDA C 编程指南》导读

https://developer.nvidia.com/blog/gpu-pro-tip-cuda-7-streams-simplify-concurrency/

====================================================

如何实现nvidia显卡的cuda的多kernel并发执行???

主要参考:GPU Pro Tip: CUDA 7 Streams Simplify Concurrency

====================================================

2022年11月11日更新

在nvidia显卡的CUDA计算中default stream是比较特殊的存在,任何没有指定的GPU上的操作都是在default stream中执行的,而default stream队列中操作的执行有一个特定就是会独占整个CPU进程在GPU端创建的context环境,也就是说default stream中的操作执行的话不论是否有其他stream队列中有操作都需要等待default stream中的操作结束才可以执行;其他non-default stream队列中如果有操作在执行,那么default stream中的操作将阻塞,直至独占整个context。如果default stream队列和non-default stream队列中都有操作,那么就会根据CPU端发送到GPU端执行命令的先后进行排队执行。

====================================================

编写多流并行(多kernel并行)的CUDA代码:(源自:GPU Pro Tip: CUDA 7 Streams Simplify Concurrency

const int N = 1 << 20;

__global__ void kernel(float *x, int n)
{
int tid = threadIdx.x + blockIdx.x * blockDim.x;
for (int i = tid; i < n; i += blockDim.x * gridDim.x) {
x[i] = sqrt(pow(3.14159,i));
}
} int main()
{
const int num_streams = 8; cudaStream_t streams[num_streams];
float *data[num_streams]; for (int i = 0; i < num_streams; i++) {
cudaStreamCreate(&streams[i]); cudaMalloc(&data[i], N * sizeof(float)); // launch one worker kernel per stream
kernel<<<1, 64, 0, streams[i]>>>(data[i], N); // launch a dummy kernel on the default stream
kernel<<<1, 1>>>(0, 0);
} cudaDeviceReset(); return 0;
}

编译:

nvcc ./stream_test.cu -o stream_legacy

使用NVIDIA Visual Profiler (nvvp)查看运行情况:

可以看到虽然在代码中将多个kernel的操作写在了不同的stream队列中,而且cuda代码运行的过程中也确实将不同的kernel操作放入到了不同的stream中执行,但是不同的stream的kernel并没有实现并行而是仍然串行。其主要原因就是不同的stream队列操作后都有一个default stream队列的操作,在默认的编译条件下default stream队列中的操作将阻塞其他stream队列中的操作,也是修改代码,剔除掉default stream队列中的操作:

// launch a dummy kernel on the default stream
        kernel<<<1, 1>>>(0, 0);

修改后代码:

const int N = 1 << 20;

__global__ void kernel(float *x, int n)
{
int tid = threadIdx.x + blockIdx.x * blockDim.x;
for (int i = tid; i < n; i += blockDim.x * gridDim.x) {
x[i] = sqrt(pow(3.14159,i));
}
} int main()
{
const int num_streams = 8; cudaStream_t streams[num_streams];
float *data[num_streams]; for (int i = 0; i < num_streams; i++) {
cudaStreamCreate(&streams[i]); cudaMalloc(&data[i], N * sizeof(float)); // launch one worker kernel per stream
kernel<<<1, 64, 0, streams[i]>>>(data[i], N); // launch a dummy kernel on the default stream
// kernel<<<1, 1>>>(0, 0);
} cudaDeviceReset(); return 0;
}

编译:

nvcc ./stream_test.cu -o stream_legacy

使用NVIDIA Visual Profiler (nvvp)查看运行情况:

可以看到在有没有default stream队列的操作后所有其他stream队列中的kernel操作实现了并行。

如果在编译cuda代码的时候加入参数--default-stream per-thread,就可以将default stream队列的操作映射到其他stream队列中,这样就不会使其他stream队列被default stream队列阻塞,代码如下(与第一个代码相同):

const int N = 1 << 20;

__global__ void kernel(float *x, int n)
{
int tid = threadIdx.x + blockIdx.x * blockDim.x;
for (int i = tid; i < n; i += blockDim.x * gridDim.x) {
x[i] = sqrt(pow(3.14159,i));
}
} int main()
{
const int num_streams = 8; cudaStream_t streams[num_streams];
float *data[num_streams]; for (int i = 0; i < num_streams; i++) {
cudaStreamCreate(&streams[i]); cudaMalloc(&data[i], N * sizeof(float)); // launch one worker kernel per stream
kernel<<<1, 64, 0, streams[i]>>>(data[i], N); // launch a dummy kernel on the default stream
kernel<<<1, 1>>>(0, 0);
} cudaDeviceReset(); return 0;
}

编译命令:

nvcc --default-stream per-thread ./stream_test.cu -o stream_per-thread

使用NVIDIA Visual Profiler (nvvp)查看运行情况:

可以看到加入编译参数--default-stream per-thread后所有的原先在default stream中的kernel操作都被映射到了stream 15队列中,并且stream 15队列中的kernel操作没有implicit隐式的与其他stream队列中的操作进行同步。

-----------------------------------------------------

如果同样的cuda操作使用CPU端多线程调用并且将每次的kernel调用都默认使用default stream队列来运行操作,那么效果如何呢?

给出代码:

#include <pthread.h>
#include <stdio.h> const int N = 1 << 20; __global__ void kernel(float *x, int n)
{
int tid = threadIdx.x + blockIdx.x * blockDim.x;
for (int i = tid; i < n; i += blockDim.x * gridDim.x) {
x[i] = sqrt(pow(3.14159,i));
}
} void *launch_kernel(void *dummy)
{
float *data;
cudaMalloc(&data, N * sizeof(float)); kernel<<<1, 64>>>(data, N); cudaStreamSynchronize(0); return NULL;
} int main()
{
const int num_threads = 8; pthread_t threads[num_threads]; for (int i = 0; i < num_threads; i++) {
if (pthread_create(&threads[i], NULL, launch_kernel, 0)) {
fprintf(stderr, "Error creating threadn");
return 1;
}
} for (int i = 0; i < num_threads; i++) {
if(pthread_join(threads[i], NULL)) {
fprintf(stderr, "Error joining threadn");
return 2;
}
} cudaDeviceReset(); return 0;
}

默认编译:

nvcc ./pthread_test.cu -o pthreads_legacy

使用NVIDIA Visual Profiler (nvvp)查看运行情况:

可以看到虽然在CPU端使用多线程调用kernel操作,但是所有的kernel操作都是使用的default stream队列,因此并不能实现多个kernel操作的GPU端并行。

如果在编译cuda代码的时候加入参数--default-stream per-thread,就可以将default stream队列的操作映射到其他stream队列中(代码与上个代码相同):

加参数编译:

nvcc --default-stream per-thread ./pthread_test.cu -o pthreads_per_thread

使用NVIDIA Visual Profiler (nvvp)查看运行情况:

可以看到加参数编译后CPU端的每个线程调用的kernel都映射到了一个新的stream队列中,实现了GPU端的多kernel并行操作。

===========================================================

看到前面的内容可以知道,想要GPU上进行多kernel的并行需要把不同的kernel操作写在不同的stream队列中,并且一定要在编译的时候加参数:--default-stream per-thread,虽然在单进程单线程的情况下不使用default stream队列存在也可以的特例。

那么参数:--default-stream per-thread是什么含义呢?

从上面的英文内容我们可以知道默认情况下每个CUDA代码在GPU上运行都会在context下有一个default stream的kernel队列,而这个default stream队列中的kernel执行会阻塞其他stream队列中的kernel操作,从而导致多个stream队列中的kernel操作无法并行。在编译的时候加入参数--default-stream per-thread,就可以使CPU端的每个线程默认调用的default stream队列映射到一个non-default stream队列中,这样就避免了因为default stream队列引起的同步阻塞。

====================================================

一个关于CUDA多流并发(多kernel并发)的PPT:

https://developer.download.nvidia.com/CUDA/training/StreamsAndConcurrencyWebinar.pdf

如何实现nvidia显卡的cuda的多kernel并发执行???的更多相关文章

  1. Ubuntu NVIDIA显卡驱动+CUDA安装(多版本共存)

    NVIDIA显卡驱动 1.禁止集成的nouveau驱动 solution 1 (recommand) # 直接移除这个驱动(备份出来) mv /lib/modules/3.0.0-12-generic ...

  2. CUDA编程接口:异步并发执行的概念和API

    1.主机和设备间异步执行 为了易于使用主机和设备间的异步执行,一些函数是异步的:在设备完全完成任务前,控制已经返回给主机线程了.它们是: 内核发射; 设备间数据拷贝函数; 主机和设备内拷贝小于64KB ...

  3. Ubuntu 16.04 + Nvidia 显卡驱动 + Cuda 8.0 (问题总结 + 解决方案)【转】

    本文转载自:https://blog.csdn.net/Zafir_410/article/details/73188228 前言 前面好一阵子忙于写论文和改论文,好久没有做新实验了,最近又回到做实验 ...

  4. NVIDIA 显卡与 CUDA 在深度学习中的应用

    CUDA(Compute Unified Device Architecture),是显卡厂商 NVIDIA 推出的运算平台. 0. 配置 显卡驱动的下载地址:Drivers - Download N ...

  5. 获取显卡的cuda算力

    获取nvidia显卡的cuda算力,在编译cuda相关代码时候可能用到. 前提: 安装了visual studio 安装了cuda(cuda应该在vs之后安装) 安装了cmake 代码 https:/ ...

  6. Kali-linux安装并配置NVIDIA显卡驱动

    显卡驱动程序就是用来驱动显卡的程序,它是硬件所对应的软件.驱动程序即添加到操作系统中的一小块代码,其中包含有关硬件设备的信息.有了此信息,计算机就可以与设备进行通信.驱动程序是硬件厂商根据操作系统编写 ...

  7. ubuntu 16.04安装nVidia显卡驱动和cuda/cudnn踩坑过程

    安装深度学习框架需要使用cuda/cudnn(GPU)来加速计算,而安装cuda/cudnn,首先需要安装nvidia的显卡驱动. 我在安装的整个过程中碰到了驱动冲突,循环登录两个问题,以至于最后不得 ...

  8. NVIDIA 显卡信息(CUDA信息的查看)

    1. nvidia-smi 查看显卡信息 nvidia-smi 指的是 NVIDIA System Management Interface: 在安装完成 NVIDIA 显卡驱动之后,对于 windo ...

  9. 安装Nvidia显卡驱动、CUDA和cuDNN的方法(jsxyhelu整编)

    Nvidia显卡驱动.CUDA和cuDNN一般都是同时安装的,这里整理的是我成功运行的最简单的方法. 一.Nvidia显卡驱动 1.1 在可以进入图形界面的情况下 直接在"软件和更新&quo ...

  10. 【CUDA开发】CUDA的安装、Nvidia显卡型号及测试

    说明:想要让Theano在Windows8.1下能利用GPU并行运算,必须有支持GPU并行运算的Nvidia显卡,且要安装CUDA,千万不要电脑上是Intel或AMD的显卡,却要编写CUDA. 文中用 ...

随机推荐

  1. 获取前(后)x月的日期

    package com.jesims.busresume.web; import org.springframework.stereotype.Service; import java.text.Da ...

  2. 新浪微博动态 RSA 分析图文+登录

    Tips:当你看到这个提示的时候,说明当前的文章是由原emlog博客系统搬迁至此的,文章发布时间已过于久远,编排和内容不一定完整,还请谅解` 新浪微博动态 RSA 分析图文+登录 日期:2016-10 ...

  3. 牛客小白月赛96(待F)

    比赛链接:牛客小白月赛96 赛时感受 赛时在前面卡的时间有点长,C题没开longlong wa了n发,D题没考虑负数又wa了n发,然后来写E的时候时间就不长了,匆忙写一次交一发. A 思路 当其中一个 ...

  4. Linux 内核:利用of_函数读取设备树结点/属性信息

    Linux 内核:利用of_函数读取设备树结点/属性信息 背景 设备树描述了设备的详细信息,这些信息包括数字类型的.字符串类型的.数组类型的,我们在编写驱动的时候需要获取到这些信息. Linux 内核 ...

  5. QT学习:03 信号与槽

    --- title: framework-cpp-qt-03-信号与槽 EntryName: framework-cpp-qt-03-signal-slot date: 2020-04-09 13:5 ...

  6. post基础错误注入

    Burpsuite抓取HTTP请求 Burpsuite是一款Web安全测试的利器,集成了几乎Web安全测试中所有需要用到的功能. 运行前提: 需要安装Java https://www.java.com ...

  7. 【ClickHouse】2:clickhouse基本语法

    背景介绍: 有三台CentOS7服务器安装了ClickHouse HostName IP 安装程序 程序端口 centf8118.sharding1.db 192.168.81.18 clickhou ...

  8. 算法金 | 推导式、生成器、向量化、map、filter、reduce、itertools,再见 for 循环

    大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 不要轻易使用 For 循环 For 循环,老铁们在编程中经常用到的一个基本结构,特别是 ...

  9. 洛谷P1077

    这道题和上一道题也是比较像的,基本采用的也是线性dp的思路 状态数组稍微有点不同,这里表示的是当前种数的花时一共的花的数量 #include<iostream> #include<u ...

  10. 全网最适合入门的面向对象编程教程:13 类和对象的Python实现-可视化阅读代码神器Sourcetrail的安装使用

    全网最适合入门的面向对象编程教程:13 类和对象的 Python 实现-可视化阅读代码神器 Sourcetrail 的安装使用 摘要: 本文主要介绍了可视化阅读代码神器Sourcetrail的安装与使 ...