环境搭建与CUDA概述

[TOC]


环境搭建与CUDA概述

Deepin15.4 CUDA环境搭建

  • CPU:i5 7300HQ
  • 显卡:gtx1050
  • 系统:deepin15.4 x64

一、安装nvidia-bumblebee实现双显卡切换

sudo apt update
sudo apt install bumblebee bumblebee-nvidia nvidia-smi

一路yes安装下去,中间可能出现坑,安装过程中,明显听到显卡风扇疯狂转动,感觉电脑电脑爆炸了,无视之,安装完成后,重启,任然爆炸感,检查显卡情况。

lspci |grep VGA # 查看显卡
lspci |grep -i nvidia # 查看nvidia设备

发现nvidia独显是关闭状态的,但是风扇爆炸……关机(不是重启),再启动,正常了,不知道什么情况。

$ nvidia-smi
$ optirun nvidia-smi
# 以上两个命令查看驱动是否安装成功
# 第一个命令会显示一个启动失败的输出
#第二个命令实际是optimize run优化运行nvidia-smi,此时会输出nvidia显卡工作状态

二、安装CUDA开发工具,基于eclipse的nsight

由于cuda版本问题,需要将gcc降到4.8版本

# 安装4.8版本gcc和g++
sudo apt install g++-4.8 gcc-4.8
# 更改软连接
cd /usr/bin
sudo rm gcc g++
sudo ln -s g++-4.8 g++
sudo ln -s gcc-4.8 gcc

下载开发工具nsight

# 分别是开发环境、工具包、IDE、性能分析工具
sudo apt install nvidia-cuda-dev nvidia-cuda-toolkit nvidia-nsight nvidia-visual-profiler
$ optirun nsight
# 启动nsight集成开发环境,一定要optirun启动,否则编译通过无法启动独显运行

了解CUDA API

  • 数据并行c++ Thrust API
  • 可用于c和c++的Runtime API
  • 可用于c和c++的Driver API

它们的区别与优缺点:

  • Thrust API提供高性能、通用性和便捷性,可理解为距离应用开发层最近的上层接口,故开发快捷,可读性强,具有较高维护性。但这也决定了它屏蔽底层硬件,无法发挥硬件全部功能。
  • Runtime API。当需要更多的底层功能以获得更好的性能的时候,可以选择放弃高层的Thrust API,选用Runtime API,它通过C语言语法扩展可获得GPGPU的所有可编程特性,因此简洁且高效。
  • Driver API。位于最底层的接口,提供更加细致的控制,这种限制不局限于队列和数据传输。使用底层的API需要调用更多函数,指定更多参数,需要检查运行时错误和兼容性问题,即存在更多开发性问题。

程序中任意部分可以自由选择使用任意一种类型的API,即可交叉使用。

/**
 * Thrust API的并行实现
 */
#include 
using namespace std;

#include 
#include 
#include 
#include 

int main_thrust(int argc, char const *argv[])
{
    const int N = 50000;

    // 创建数组,GPU设备中
    thrust::device_vector a(N);
    // 填充数组,First元素值为0
    thrust::sequence(a.begin(),a.end(),0);
    // 并行计算数组元素和
    int sumA = thrust::reduce(a.begin(),a.end(),0);
    // 串行计算0到N-1的和
    int sumB = 0;
    for(int i=0;i

编译运行

$ nvcc seqCuda.cu -o seqCuda
$ optirun ./seqCuda
/**
 * runtime API并行实现
 */

#include 
using namespace std;

#include 
#include 
#include 
#include 

/**
 * GPU代码,runtime API
 */
__global__ void fillKernel(int *a,int n){
    /**
     * 计算线程id
     * blockIdx.x=线程块序号
     * blockDim.x=每个线程块内的或维度线程数量???
     * threadIdx.x=用于定位该线程在线程块内的编号
     */
    int tid = blockIdx.x*blockDim.x+threadIdx.x;
    // 可能分配的线程数量多于N,这里只操作N范围内的线程
    if(tid>>(d_a,n);
}

int main_runtime() {
    const int N = 50000;
    // 创建数组
    thrust::device_vector a(N);
    // 填充数组,用runtime api
    fill(thrust::raw_pointer_cast(&a[0]),N);
    // 计算数组元素和,thrust api
    int sumA = thrust::reduce(a.begin(),a.end(),0);
    // 串行计算和
    int sumB = 0;
    for(int i=0;i

编译运行

$ nvcc seqRuntime.cu -o seqRuntime
$ optirun ./seqRuntime

基本概念

  • GPGPU:General Purpose Graphics Processing Unit(GPU),GPU作为独立设备外接在主机系统(Host)上,GPGPU与主机处理器并行运转,同时各自处理各自的计算任务。PCIe总线用来在设备间传输数据和命令。

  • CUDA数据传输方式:

    • cudaMemcpy()进行显式数据传输(Thrust API中直接通过host的data和gpu的data直接赋值实现,d_a = h_a;//host数据赋值给device数据
    • 通过页锁定内存的映射进行隐式数据传输。该接口维护主机内存区域和gpu设备内存区域,自动完成数据同步不需要人工干预。这种方式可以提高程序执行效率,可实现零拷贝操作(但是,这种映射的访问会触发IO的瓶颈吗?)。
  • host和GPU device通过驱动程序driver通信,包括数据、命令的通信,以及内存映射、缓冲、队列和同步功能。

  • GPGPU运行在一个和主处理器隔离的存储空间,GPGPU有独立的内存空间,且GPU的内存带宽远远高于CPU的内存带宽。CUDA为不同设备之间提供了一个统一虚拟编址UVA,使各设备的代码可通过一个指针访问其他设备的数据(在一个空间中寻址)。

  • CUDAKernel。CUDAKernel是在host中调用而运行在CUDA设备上的子程序,它没有返回值,通过__global__来定义,kernel由主处理器调用。

  • Kernel的调用是异步的,主机把GPU要执行的kernel顺序提交给GPGPU,然后不等待其执行完成,直接去执行后续的CPU代码,而GPGPU则同时开始并行执行kernel。这样需要一种同步机制给主调方(Host,CPU)和执行方(Device,GPU),CUDA提供两种同步方式:

    • 主机显式调用cudaThreadSynchronize()函数,使主机进入阻塞,等待所有提交的kernel执行完成。
    • 利用cudaMemory()实现阻塞式数据传输——实际在cudaMemory内部会调用cudaThreadSynchronize。
  • GPU上基本运行单位是线程,各线程相隔离。执行配置定义执行kernel所需的线程数量,同时包含1D、2D或3D计算网格中各维度的分配。执行配置用<<< 配置 >>>包裹在函数名与参数之间。

  • CPU上最大可共享的内存区域称为全局内存,它是GB级别的RAM,但相比访问寄存器,访问全局内存的IO延时非常高,因此要注意数据的重用。

  • 数据并行与任务并行。数据并行称为循环级并行,循环内操作数据的并行。任务并行是另一种并行方式,通过将多个任务并发执行来减少执行时间。

GPGPU编程三条法则

  • 交给GPGPU足够多的任务,让它繁忙,增加系统并行度。
  • 将数据放入并始终存储于GPU,CPU和GPU之间的PCIe的传输速度相比GPU内部的内存访问速度要慢的太多。尽量避免运行中数据用于传输的损耗。
  • 注重GPGPU上的数据重用,避免带宽限制。这里是避免内部全局内存的瓶颈。

需要了解GPU内存的分布、使用机制,了解GPU线程的机制,以及CPU和GPU之间数据同步的过程。

CUDA和Amdahl定律

Amdahl是一种用以估算串行程序并行化之后的理想加速比,前提条件是并行化后问题尺度保持不变。

$$S(n)=\frac{1}{(1-p)+p/n}$$

其中,p为程序中可并行化的部分,分配给n个处理器上执行。

混合执行

同时使用CPU和GPU资源。多核处理器也是一种支持数据并行和任务并行的设备。OpenMP(open multi-processing)提供用于宿主处理器进行多线程程序开发的编程方式。nvcc编译器也支持OpenMP。

/**
 * CPU/GPU异步执行源代码
 * 其中CPU代码通过openMP多核并行执行
 */
#include 
using namespace std;

#include 
#include 
#include 
#include 

int main(){
    const int N = 50000;
    // 创建数组
    thrust::device_vector a(N);
    // 填充数组,thrust GPU并行
    thrust::sequence(a.begin(),a.end(),0);
    // OpenMP CPU并行计算和
    int sumB = 0;
#pragma omp parallel for reduction(+:sumB)
    for(int i=0;i

编译运行

$ nvcc -O3 -Xcompiler -fopenmp seqAsync.cu -o seqAsync
$ optirun ./seqAsync

你可能感兴趣的:(环境搭建与CUDA概述)