1. 程式人生 > >CUDA 學習(二)

CUDA 學習(二)

CUBLAS_Library.pdf 學習記錄

Chapter 1. INTRODUCTION

從CUDA6.0之後,cuBLAS 庫有兩套API,第一個稱為cuBLAS API,第二個是CUBLASXT API,這個文件講的是cuBLAS API。
使用cuBLAS API ,應用一定要在GPU的記憶體空間中分配需要的矩陣和向量,並進行賦值,然後呼叫適用的cuBLAS函式,之後從GPU記憶體讀取資料到host端。cuBLAS API 提供了讀寫GPU記憶體的幫助函式。
使用CUBLASXT API,應用需要把資料放在HOST,然後庫會根據使用者的要求將資料排程到GPUS的處理上。

1.1. Data layout

為了最大限制滿足已有的Fortran 環境,cuBLAS 採用的是列優先的儲存方式和基於1的檢索。
這裡寫圖片描述
但是,c和c++用的是行優先儲存,因此用巨集定義(maros)或者行內函數(inline)。Fortran移植到c語言裡面,為了保持基於1檢索避免在迴圈中過多的操作,這種情況,對於矩陣row i 和 column j 可以通過下面的巨集實現

#define IDX2F(i,j,ld) ((((j)-1)*(ld))+((i)-1))

這裡,ld表示的是矩陣的主要尺寸(the leading dimension of the matrix,比如是列優先,ld表示行數)(翻譯很奇怪)。反之用下面這個巨集

#define IDX2C(i,j,ld) (((j)*(ld))+(i))

1.2. New and Legacy cuBLAS API

從4.0版本之後,cuBLAS庫提供傳統的API的同時也提供新更新的API。這個地方將介紹新提供的API和它的優點以及傳統的API的區別。
通過加下面的標頭檔案使用新的API:

#include "cublas_v2.h"

下面的特徵是 傳統API沒有的:
1、cuBLAS的上下文控制代碼用函式初始化並且被傳遞到後續每一個使用的庫函式。這使得使用者在多主機執行緒和多GPU時對庫的設定有更多的控制權。這使得cuBLAS API可以重入(reentrant)。
2、標量α

 β 可以通過在主機或者裝置中宣告然後進行傳遞,而不是在主機上被分配然後通過值進行傳遞。這個改變使得庫函式可以用流進行執行同步,即使α β  是在前面的核產生的。
3、當一個庫函式返回一個標量結果,它可以通過一個在主機或者裝置宣告的變數返回,而不是返回主機中的數值。這個使得庫函式可以直接使用返回值。
4、錯誤狀態 cublasStatus_t,更好除錯。
5、the cublasAlloc() and cublasFree() functions have been deprecated. This change removes these unnecessary wrappers around cudaMalloc() and cudaFree(), respectively.
5、cublasSetKernelStream() 從命名為cublasSetStream()

傳統的cuBLAS API 使用時候包含以下標頭檔案:

#include "cublas.h"

1.3. Example code

下面的兩個使用cuBLAS 庫的c程式,用了兩種不同的索引風格:
(Example 1. “1-based indexing” and Example 2.”0-based Indexing”)

//Example 1. Application Using C and CUBLAS: 1-based indexing
//-----------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <cuda_runtime.h>
#include "cublas_v2.h"
#define M 6
#define N 5
#define IDX2F(i,j,ld) ((((j)-1)*(ld))+((i)-1)) 
static __inline__ void modify (cublasHandle_t handle, float *m, int ldm, int
n, int p, int q, float alpha, float beta){
    cublasSscal (handle, n-p+1, &alpha, &m[IDX2F(p,q,ldm)], ldm);
    cublasSscal (handle, ldm-p+1, &beta, &m[IDX2F(p,q,ldm)], 1);
}
int main (void){
    cudaError_t cudaStat;
    cublasStatus_t stat;
    cublasHandle_t handle;
    int i, j;
    float* devPtrA;
    float* a = 0;
    a = (float *)malloc (M * N * sizeof (*a));
    if (!a) {
        printf ("host memory allocation failed");
        return EXIT_FAILURE;
    }
    for (j = 1; j <= N; j++) {
    for (i = 1; i <= M; i++) {
        a[IDX2F(i,j,M)] = (float)((i-1) * M + j);
    }
    }
    cudaStat = cudaMalloc ((void**)&devPtrA, M*N*sizeof(*a));
    if (cudaStat != cudaSuccess) {
        printf ("device memory allocation failed");
        return EXIT_FAILURE;
    }
    stat = cublasCreate(&handle);
    if (stat != CUBLAS_STATUS_SUCCESS) {
        printf ("CUBLAS initialization failed\n");
        return EXIT_FAILURE;
    }
    stat = cublasSetMatrix (M, N, sizeof(*a), a, M, devPtrA, M);
    if (stat != CUBLAS_STATUS_SUCCESS) {
        printf ("data download failed");
        cudaFree (devPtrA);
        cublasDestroy(handle);
        return EXIT_FAILURE;
    }
    modify (handle, devPtrA, M, N, 2, 3, 16.0f, 12.0f);
    stat = cublasGetMatrix (M, N, sizeof(*a), devPtrA, M, a, M);
    if (stat != CUBLAS_STATUS_SUCCESS) {
        printf ("data upload failed");
        cudaFree (devPtrA);
        cublasDestroy(handle);
        return EXIT_FAILURE;
    }
    cudaFree (devPtrA);
    cublasDestroy(handle);
    for (j = 1; j <= N; j++) {
        for (i = 1; i <= M; i++) {
            printf ("%7.0f", a[IDX2F(i,j,M)]);
        }
        printf ("\n");
    }
    free(a);
    return EXIT_SUCCESS;
}

執行結果
這裡寫圖片描述

Chapter 2. USING THE CUBLAS API

2.1. General description

2.1.1. Error status

所有的cuBLAS庫函式都返回the error status cublasStatus_t

2.1.2. cuBLAS context

應用必須通過呼叫cublasCreate()函式來初始化cuBLAS 庫的上下文控制代碼。通過cublasDestory()來釋放。應用程式可以使用cudaSetDevice()結合初始化的獨特上下文控制代碼,應用程式根據不同的控制代碼將資料傳遞到不同的裝置進行計算。如果在一個主機裡面使用不同的設定,在使用新裝置前呼叫cudaSetDevice(),然後cublasCreate()根據當前的設定裝置來初始化不同的上下文控制代碼。

2.1.3. Thread Safety

庫的執行緒安全即使它被同一個控制代碼的多個主機程序呼叫。當多個執行緒使用同一個控制代碼,需要注意,當 控制代碼被改變時候,這個改變會影響到後續使用它的執行緒。因此不建議多個程序同時時候同一個CUBLAS 控制代碼。

2.1.4. Results reproducibility

By design, all CUBLAS API routines from a given toolkit version, generate the same bitwise results at every run when executed on GPUs with the same architecture and the same number of SMs. However, bit-wise reproducibility is not guaranteed across toolkit version because the implementation might differ due to some implementation changes.
For some routines such as cublassymv and cublashemv, an alternate significantly faster routines can be chosen using the routine cublasSetAtomicsMode(). In that case, the results are not guaranteed to be bit-wise reproducible because atomics are used for the computation.

2.1.5.Scalar Parameters

有兩種類別的函式使用標量引數:
1、將在主機或者裝置內定義的α 或者β  作為變化因子的函式,如gemm
2、返回在裝置或者主機上的標量的函式,如 amax(), amin(), asum(), rotg(), rotmg(), rotmg(), dot() 和 nrm2()。
對於第一種類別,當指標模式設定為”CUBLAS_POINTER_MODE_HOST”,這種情況標量α 或者β 可以在棧或者分配在堆中。Underneath the CUDA kernels related tothat functions will be launched with the value of alpha and/or beta. 因此如果他們在堆中被分配,他們可以在呼叫放回後被釋放即使核心是非同步的。當指標的模式設定為“CUBLAS_POINTER_MODE_DEVICE”α 或者β  應該在裝置上可以被訪問並且不能被修改直到核呼叫完成。注意由於cudaFree隱式呼叫cudaDeviceSynchronize(),cudaFree() can still be called on alpha and/or beta just after the call but it would defeat the purpose of using this pointer mode in that case.
第二種模式:指標模式為”CUBLAS_POINTER_MODE_HOST”,GPU計算完成後結果會賦值回Host。指標模式設定為 “CUBLAS_POINTER_MODE_DEVICE“這函式立即返回。這種情況,和矩陣或者向量結果相似,標量結果只有等例程在GPU上完成。為了從主機裡面讀結果,這個就要求正確的同步。
在任何一種情況,指標模式為“CUBLAS_POINTER_MODE_DEVICE“庫函式允許在完全非同步時執行,即使alpha 和 beta是前一個核產生。比如,當用cuBLAS庫迴圈的方法解決線性系統和特徵值的問題是會出現。

2.1.6.平行流