CUDA系列(7) 使用MPS技術提升GPU利用率及多進程CUDA程序的性能

2021-03-02 子棐之GPGPU
在學習MPS技術之前,我們先來了解Hyper-Q的概念。Hyper-Q即Hyper Queue,從Kepler開始,GPU上均具備該特性。它允許多個Kernel任務在GPU中並發執行。多Kernel的組織形式,既可以是1個線程內的多個流(Multi-stream),也可以是1個進程內的多個線程(Multi-thread),或者是多個進程(Multi-process)。

在使用CUDA Runtime API進行編程時,會在第一次調用CUDA runtime函數時自動(隱式的)創建CUDA Context(Primary Context)。CUDA Context中包含了Process中所有的狀態,例如顯存的映射/分配,Kernel函數定義,以及其他相關狀態信息。每個Process都有自己的CUDA Context並對應1個GPU設備,並且其不能在多個Process之間共享。

對於1個Process內使用multi-stream或者multi-thread方式進行多Kernel並發的情況,因為它們在同一CUDA context內,Hyper-Q特性允許同一時刻有多個kernel同時使用GPU資源且並發地執行。當單個Kernel佔用的GPU資源較少時,這種方式能夠有效的提升GPU的利用率,並且在時間維度上縮短整個程序執行時間(相比於多個Kernel串行執行),提升任務吞吐能力。

對於多個Process進行多Kernel並發的情況,由於它們都有各自的CUDA Context,雖然Hyper-Q特性也支持將多個Context同時放在GPU上執行,但GPU並不能在同一時間內同時執行多個context內的Kernel,它們需要在GPU上進行時間片輪轉調度,同時要做Context switch。程序總的執行時間,與多個kernel串行執行的時間總和相等或更長。

MPS是Multi-Process Service的縮寫。它是CUDA API中的一項特殊功能,支持在具有Hyper-Q功能的GPU(Kepler/Maxwell/Pascal/Volta…)上實現多進程CUDA程序(特別是基於MPI的多任務程序)的並發執行。

也就是說,對於上述multi-process無法真正實現多個kernel並行執行的情況,開啟MPS服務能夠幫助我們解決該問題。MPS能夠將多個Process的CUDA context整合成一個,也就是說不需要再進行GPU上的時間片輪轉以及context switch,這與使用單個Process內multi-stream方式做kernel並發的效率是一樣的。而且,不能忽視的是,由於使用了多進程,CPU端代碼的執行效率也提高了。這對於很多線上服務來說非常簡單高效。

MPS服務開啟的步驟如下:

1) 只可見需要運行MPS服務的GPU,例如0號設備:export CUDA_VISIBLE_DEVICES=0

2) 設置GPU為exclusive process計算模式(需要sudo權限):sudo nvidia-smi -i 0 -c EXCLUSIVE_PROCESS


註:此步驟並非必須的。不開啟exclusive模式也可以使用MPS。開啟該功能的目的只是確保1塊GPU上只有1個process context。

3) 啟動MPS服務:nvidia-cuda-mps-control -d

4) 之後,在另一個窗口中(client端),使用mpirun執行多進程。

5) 查看mps deamon及服務:ps -ef | grep mps

還沒執行過process時,只有deamon在運行:

如果執行過process,則server會被啟動,並且一直留在系統中:

1) 停止MPS服務:echo quit | nvidia-cuda-mps-control

2) 查看deamon及server是否已關閉:ps -ef | grep mps

3) 將compute mode恢復為默認:sudo nvidia-smi -i 0 -c 0

OS:Ubuntu 16.04

CUDA:9.1.85

GPU:TITAN V *

*在不同架構不同型號GPU以下測試取得的結果略有不同。

__global__ void testMaxFlopsKernel(float * pData, int nRepeats,   float v1, float v2)

{

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

        float s =   pData[tid], s2 = 10.0f - s, s3 = 9.0f - s, s4 = 9.0f - s2;

        for(int i = 0; i   < nRepeats; i++)

        {

            s=v1-s*v2;

            s2=v1-s1*v2;

            s3=v1-s2*v2;

            s4=v1-s3*v2;

        }

        pData[tid] =   ((s+s2)+(s3+s4));

}

int nRepeats = 1000000000; // repeat computing number per thread

dim3 blockSize(1, 1, 1); // block size

dim3 gridSize(1,1,1); // grid size

testMaxFlopsKernel<<<gridSize,   blockSize>>>(fpData_d, nRepeats, 10.0f, 9.9899f);

為了簡單易理解,我們只設置kernel發射1個thread,設置的「nRepeats」較大的目的是讓kernel函數執行時間儘可能長,這樣利用觀察多個process的kernel之間進行overlap。

2個Kernel的執行時間分別為6854ms,6838ms,幾乎是單個Kernel執行時長的2倍。正如前文所述,因為kernel在GPU上是被時間片輪轉調度執行的,相當於2個kernel是在交替串行執行。

        4個Kernel執行時間分別為13952,13947ms,13934ms,13940ms。結論同上。

2個Kernel執行時間分別為3467ms,3467ms,與單個Kernel執行的時間基本相同。多個Kernel之間真正實現了並行執行。

4個Kernel的執行時間分別為3505ms,3492ms,3492ms,3492ms。結論同上。

分析工具:nvprof + nvvp

1) 啟動nvprof:nvprof --profile-all-processes -o output.%p

打開另一個ssh窗口,執行mpirun啟動多進程執行:

回到nvprof窗口,可以看到已經生成了2個process的output文件,此時按「ctrl+c」可以退出nvprof(不退出亦可):

2) 打開nvvp,在file菜單選擇import,然後選擇nvprof;

Next後,選擇multi process:

Next,Browse選擇output文件:

點擊Finish,即可得到timeline結果。具體參見下面的具體分析結果。

結論:可以看到4個Process發射的4個Kernel之間可以concurrent執行,但並不是真正意義上的overlap。實際上,每個Process發射的Kernel在自己的執行周期內都佔用了整個GPU(雖然只有1個thread),而多個Kernel之間採用時間片輪轉調度的方式在GPU上執行,所以每個Kernel的執行都被「拉長」了,而多個Kernel並發後總的執行時間約等於每個Kernel串行執行時間的總和。

結論:可以看到4個Process發射的4個Kernel之間可以concurrent執行,並且是overlap的。每個Kernel的執行時間基本等於單個Process發射該kernel的執行時間。而多個Kernel並發後(前提是每個Lernel佔用的GPU計算和存儲資源都不大)總的執行時間約等於單個Kernel執行時間,真正實現了並發執行,充分利用GPU資源的同時,減少了多進程總的執行時間,提升了吞吐性能。

相關焦點

  • CUDA之CUDA編程模型概述(一)
    編程模型就是告訴我們如何寫CUDA程序,如果做過C開發的同學或者其他開發的同學都知道做個完整的項目不只是寫代碼,還有需求分析,調試,優化,部署等一些列步驟。CUDA平臺也提供了著一些列的工具供我們使用,我們這一章主要就是講解這些工具怎麼用,如何編寫調試CUDA程序。以及編寫兩個矩陣運算有關的CUDA應用,以供大家把玩。
  • 寫CUDA到底難在哪?
    而這個數據分組的方式,存儲方法等等直接的影響到你這個並行程序最終的性能。恰恰這一塊又是普通沒有經過訓練的程式設計師平時完全不考慮的地方。而現代複雜的GPU異構系統導致這個數據分組異常的難。比如要使用一個多GPU的機器,你要把數據分成每個GPU的大組,每個GPU要分成每個Thread的小組,然後為了使用張量(Tensor)核心還要分成張量處理器能用的固定數量的小組,這個過程簡直是難上艱難,燒腦子掉頭髮。
  • 《NVIDIA CUDA 開發者系列培訓》筆記(二)
    本系列筆記為個人學習筆記,課程為《NVIDIA CUDA 開發者系列培訓》欲瀏覽課程教程視頻可點擊原文連結。Writing parallel kernels在開始寫kernels之前,我們需要對CUDA線程的兩層結構有所了解。一個線程是一個序列的執行單元,特點是所有的線程會執行相同的代碼,而且是並行執行的。
  • GPU並行編程:熟練使用CUDA C語言
    這並不是什麼新概念,並行計算已經存在多年,PC使用多個CPU並行處理任務,提高不同應用程式的執行速度,你可以將上面提到的「人」看作一個進程或一個線程,計算機可以將每個進程分配給不同的處理器,接收到任務的所有處理器並行執行一個任務(計算)。
  • CUDA系列學習(二)(轉)
    (三)、CUDA Context一個CUDA Context類似於一個CPU進程。程序在Initialization的時候,runtime給每個device創建一個CUDA context,這個context在所有host threads中共享。
  • 【CUDA學習筆記】第五篇:內存以及案例解釋(附案例代碼下載方式)
    話不多說,直接coding吧:#include "stdio.h"#include<iostream>#include <cuda.h>#include <cuda_runtime.h>__constant__ int constant_f;__constant__ int constant_g
  • 讓 Windows 的 R 用上 CUDA
    R 是一個統計學經常用到的軟體,提供了非常多的統計學函數。 但是它是一個單線程解釋語言,面對大數據量的時候,往往性能跟不上,可以利用 Rcpp 編寫 C++ 包提供給 R 使用,可以大大提高性能。 而對於大規模數據的處理,使用 CUDA 則是一個非常好的解決方案。
  • CUDA優化的冷知識 8 |GPU顯存的特色
    )大家可以訪問:https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html 來閱讀原文。大部分的CUDA代碼在追求極致性能的時候, 所面臨的主要矛盾, 往往是在顯存訪問上. (而不是在其他的幾個矛盾, 例如計算性能上), 不過這是不一定的, 雖然大部分的GPU程序可能如此, 但是很多還是強烈的需求計算性能的, 而不一定短板是在顯存的帶寬之類的上. 而大部分GPU程序只所以如此, 往往來源於以下3個方面:(1) NV在生產顯卡的時候, 往往搭配一定的計算性能和顯存性能.
  • PyTorch中使用DistributedDataParallel進行多GPU分布式模型訓練
    雖然GPT是一個比較極端的例子但是各種SOTA模型正在推動越來越大的模型進入生產應用程式,這裡的最大挑戰是使用GPU卡在合理的時間內完成模型訓練工作的能力。為了解決這些問題,從業者越來越多地轉向分布式訓練。 分布式訓練是使用多個GPU和/或多個機器訓練深度學習模型的技術。
  • 解讀CUDA C Programming Guide 第三章第2節之Initialization
    Compilation with NVCCCUDA C RuntimeVersioning and CompatibilityCompute ModesMode SwitchesTesla Compute Cluster Mode for Windows由於第三章第2小節包含的內容非常多,
  • CUDA編程學習系列1
    __global__void add(int n, float *x, float *y){ for (int i = 0; i < n; i++) y[i] = x[i] + y[i];}在函數中調用時,也區別於原來的調用方式,是以下面方式:其次,在CUDA中申請內存方式與CPU也有所不一樣,在這裡我們使用以下函數
  • C++ 使用類調用 CUDA 核函數
    (給CPP開發者加星標,提升C/C++技能)導讀:CUDA是用於GPU編程的框架,在深度學習高速發展的今天
  • Numba:用CUDA加速的高性能Python編譯器
    但是Python的最大優點也可能是它最大的缺點:它的靈活性和無類型的高級語法會導致數據和計算密集型程序的性能不佳。出於這個原因,關心效率的Python程式設計師經常在C中重寫他們的最內層的循環,並從Python調用編譯的C函數。有許多項目旨在簡化這種優化,例如Cython,但這往往需要學習一種新的語法。
  • CUDA在MFC中的聯調方法實例
    【IT168 技術】本文我們為大家介紹CUDA與MFC聯調的實現。  MFC中應用CUDA程序  本章建立一個MFC工程,然後在該工程中添加對cu文件中CUDA程序的調用。
  • cuda入門:如何進行矩陣乘法優化
    【IT168 技術】前面介紹的計算平方和的程序,似乎沒有什麼實用價值。所以我們的第二個 CUDA 程序,要做一個確實有(某些)實用價值的程序,也就是進行矩陣乘法。而且,這次我們會使用浮點數。  雖然矩陣乘法有點老套,不過因為它相當簡單,而且也可以用來介紹一些有關 CUDA 的有趣性質。
  • Numba:基於CUDA加速的高性能Python
    但Python最大的優勢也可能是其最大的弱點:其靈活性和無類型的高級語法可能導致數據和計算密集型程序的性能不佳。因此,關心效率的Python程式設計師通常會用C重寫最內層的循環,並從Python調用編譯好的C函數。有許多旨在使此優化更容易的項目(如Cython),但它們通常需要學習新的語法。
  • 用cmake搭建環境來編譯一個CUDA程序
    【IT168 文檔】一、簡介  在Windows平臺下,用cmake來搭建環境環境,在VS2005下運行CUDA程序。其實,在Windows下,在CUDA2.3的SDK裡,有一個Cuda.Rules的文件。通過這個文件,在VS2005裡可以很方便的設置各個編譯參數。
  • 利用CUDA加速k近鄰算法
    CUDA ZONE專區:http://cuda.it168.com/  CUDA技術論壇:http://cuda.itpub.net  在接觸CUDA之前,已經聽說過可以利用GPU來加速一些通用計算,也有不少人研究GPU上的通用計算(GPGPU)。
  • cuda 安裝 小記.
    linux 桌面安裝:https://aws.amazon.com/cn/premiumsupport/knowledge-center/connect-to-linux-desktop-from-windows/教訓: cuda
  • 【CUDA學習筆記】第一篇:一個基本的CUDA C程序(附配置方法和安裝包下載方式)
    CUDA可以使用簡單的編程API在圖形處理單元(GPU)上創建大規模並行應用程式。    使用C和C++的軟體開發人員可以通過使用CUDA C或C++來利用GPU的強大性能來加速他們的軟體應用程式。用CUDA編寫的程序類似於用簡單的C或C++編寫的程序,添加需要利用GPU並行性的關鍵字。CUDA允許程式設計師指定CUDA代碼的哪個部分在CPU上執行,哪個部分在GPU上執行。