Fungsi-fungsi Pembantu CUBLAS

Dari PaloDozen

Selain menyediakan fungsi-fungsi BLAS dalam versi CUDA, pustaka CUBLAS juga menyediakan beberapa fungsi pembantun yang dapat digunakan untuk memudahkan penggunaan pustaka CUBLAS.

Daftar isi

cublasInit()

Sintaks:

cublasStatus cublasInit(void)

Fungsi ini melakukan inisialisasi pustaka CUBLAS dan harus dipanggil sebelum pemanggilan API CUBLAS yang lain. Fungsi ini akan mengalokasikan sumber daya perangkat keras yang diperlukan untuk mengakses GPU. Fungsi ini akan menempelkan CUBLAS ke GPU yang sedang terikat pada thread host di mana fungsi ini dipanggil.

Nilai kembalian:

  • CUBLAS_STATUS_ALLOC_FAILED, jika sumber daya perangkat keras tidak bisa dialokasikan
  • CUBLAS_STATUS_SUCCESS, jika pustaka CUBLAS berhasil diinisialisasi.

cublasShutdown()

Sintaks:

cublasStatus cublasShutdown(void)

Fungsi ini akan melepaskan sumber daya sisi CPU yang digunakan oleh pustaka CUBLAS. Pelepasan sumber daya pada sisi GPU dapat ditunda sampai aplikasi berhenti.

Nilai kembalian:

  • CUBLAS_STATUS_NOT_INITIALIZED, jika pustaka CUBLAS tidak terinisialisasi.
  • CUBLAS_STATUS_SUCCESS, jika pustaka CUBLAS berhasil dihentikan.

cublasGetError()

Sintaks:

cublasStatus cublasGetError(void)

Fungsi ini akan mengembalikan error terakhir yang terjadi pada pemanggilan suatu fungsi inti CUBLAS. Fungsi pembantu CUBLAS mengembalikan status secara langsung, sedangkan fungsi CUBLAS inti tidak dengan maksud menjada kompatibilitas dengan fungsi BLAS yang tidak mengembalikan status. Pembacaan status melalui cublasGetError() akan mereset keadaan error internal kembali menjadi CUBLAS_STATUS_SUCCESS.

cublasAlloc()

Sintaks:

cublasStatus cublasAlloc(int n, int elemSize, void **devicePtr)

Fungsi ini membuat suatu objek dalam memori GPU yang dapat menyimpan suatu array dengan n elemen, di mana masing-masing elemen memerlukan elemSize byte. Jika pemanggilan fungsi berjalan dengan sukses, suatu pointer ke objek dalam memori GPU akan diletakkan pada devicePtr. Perhatukan bahwa devicePtr merupakan pointer pada GPU yang tidak dapat didereferensi pada kode host. Fungsi cublasAlloc() adalah suatu wrapper dari fungsi cudaMalloc. Pointer device yang dikembalikan oleh cublasAlloc() dapat dilemparkan kepada sembarang kernel device CUDA, bukan hanya fungsi CUBLAS.

Nilai kembalian:

  • CUBLAS_STATUS_NOT_INITIALIZED, jika pustaka CUBLAS tidak terinisialisasi.
  • CUBLAS_STATUS_INVALID_VALUE, jika n <= 0 atau elemSize <= 0.
  • CUBLAS_STATUS_ALLOC_FAILED, jika objek tidak dapt dialokasikan akibat kekurangan sumber daya.
  • CUBLAS_STATUS_SUCCESS|, jika alokasi objek berhasil dilakukan.

cublasFree

Sintaks:

cublasStatus cublasFree(const void *devicePtr)

Fungsi ini membebaskan (dealokasi) memori pada GPU yang direferensi oleh devicePtr.

Nilai kembalian:

  • CUBLAS_STATUS_NOT_INITIALIZED, jika pustaka CUBLAS tidak terinisialisasi.
  • CUBLAS_STATUS_INTERNAL_ERROR, jika memori tidak dapat didealokasikan.
  • CUBLAS_STATUS_SUCCESS, jika objek berhasil didealokasikan.

cublasSetVector()

Sintaks:

cublasStatus cublasSetVector(int n, int elemSize, const void *x, 
                             int incx, void *y, int incy)

Fungsi ini meng-copy elemen dari suatu vektor x dalam memori CPU ke suatu vektor y dalam memori GPU. Elemen-elemen pada kedua vektor diasumsikan memiliki ukuran elemSize byte. Jarak penyimpanan antara elemen yang berurutan adalah incx untuk vektor sumber x dan incy untuk vektor tujuan y. Secara umum, y menunjuk pada suatu objek, atau bagian dari suatu objek, yang dialokasikan melalui cublasAlloc(). Untuk matriks digunakan column-major format. Jika suatu vektor merupakan bagian dari suatu matriks, kenaikan 1 pada vektor akan mengakses suatu (sebagian) kolom pada matriks. Sedangkan kenaikan sebesar dimensi awal dari matriks akan mengakses suatu (sebagian) baris.

Nilai kembalian:

  • CUBLAS_STATUS_NOT_INITIALIZED, jika pustaka CUBLAS tidak terinisialisasi.
  • CUBLAS_STATUS_INVALID_VALUE, jika incx, incy, atau elemSize <= 0
  • CUBLAS_STATUS_MAPPING_ERROR, jika ada error dalam mengakses memori GPU.
  • CUBLAS_STATUS_SUCCESS, jika operasi berhasil dilakukan.

cublasGetVector()

Sintaks:

cublasStatus cublasGetVector(int n, int elemSize, const void *x,
                             int incx, void *y, int incy)

Fungsi ini meng-copy n elemen dari suatu vektor x dalam memori GPU ke suatu vektor y dalam memori CPU. Elemen dalam kedua vektor diasumsikan memiliki ukuran elemSize byte. Jarak penyimpanan antara elemen yang berurutan adalan incx untuk vektor sumber x dan incy untuk vektor tujuan y. Secara umum, y menunjuk pada suatu objek, atau bagian dari suatu objek, yang dialokasikan melalui cublasAlloc(). Untuk matriks digunakan column-major format. Jika suatu vektor merupakan bagian dari suatu matriks, kenaikan 1 pada vektor akan mengakses suatu (sebagian) kolom pada matriks. Sedangkan kenaikan sebesar dimensi awal dari matriks akan mengaksis suatu (sebagian) baris.

Nilai kembalian:

  • CUBLAS_STATUS_NOT_INITIALIZED, jika pustaka CUBLAS tidak terinisialisasi.
  • CUBLAS_STATUS_INVALID_VALUE, jika incx, incy, atau elemSize <= 0
  • CUBLAS_STATUS_MAPPING_ERROR, jika ada error dalam mengakses memori GPU.
  • CUBLAS_STATUS_SUCCESS, jika operasi berhasil dilakukan.

cublasSetMatrix()

Sintaks:

cublasStatus cublasSetMatrix(int rows, int cols, int elemSize,
                             const void *A, int lda, void* B, ldb)

Fungsi ini meng-copy rows x cols elemen dari suatu matriks A dalam memori CPU ke suatu matriks B dalam memori GPU. Masing-masing elemen memerlukan ukuran penyimpanan sebesar elemSize byte. Kedua matriks diasumsikan menggunakan column-major format, dengan dimensi awal (yakni jumlah baris) dari matriks sumber A disediakan pada variabel lda dan dimensi awal pada matriks tujuan B adalah pada ldb. B adalah pointer device yang menunjuk pada suatu objek, atau bagian dari suatu objek, yang dialokasikan dalam memori GPU melalui cublasAlloc().

Nilai kembalian:

  • CUBLAS_STATUS_NOT_INITIALIZED, jika pustaka CUBLAS tidak terinisialisasi.
  • CUBLAS_STATUS_INVALID_VALUE, jika rows atau cols<0; atau elemSize, lda, atau ldb<=0.
  • CUBLAS_STATUS_MAPPING_ERROR, jika terjadi error dalam mengakses memori GPU.
  • CUBLAS_STATUS_SUCCESS, jika operasi berhasil dilakukan.

cublasGetMatrix()

Sintaks:

cublasStatus cublasGetMatrix(int rows, int cols, int elemSize,
                             const void *A, int lda, void *B, ldb)

Fungsi ini meng-copy rows x cols elemen dari suatu matriks A dalam memori GPU ke suatu matriks B dalam memori CPU. Masing-masing elemen memerlukan ukuran penyimpanan sebesar elemSize byte. Kedua matriks diasumsikan menggunakan column-major format, dengan dimensi awal (yakni jumlah baris) dari matriks sumber A disediakan pada variabel lda dan dimensi awal pada matriks tujuan B adalah pada ldb. B adalah pointer device yang menunjuk pada suatu objek, atau bagian dari suatu objek, yang dialokasikan dalam memori GPU melalui cublasAlloc().

Nilai kembalian:

  • CUBLAS_STATUS_NOT_INITIALIZED, jika pustaka CUBLAS tidak terinisialisasi.
  • CUBLAS_STATUS_INVALID_VALUE, jika rows atau cols<0; atau elemSize, lda, atau ldb<=0.
  • CUBLAS_STATUS_MAPPING_ERROR, jika terjadi error dalam mengakses memori GPU.
  • CUBLAS_STATUS_SUCCESS, jika operasi berhasil dilakukan.

Contoh Penggunaan (Mode Emulasi)

Kode berikut ini mendemonstrasikan penggunaan fungsi-fungsi pembantu CUBLAS. Alur program yang digunakan cukup sederhana, yakni membuat suatu array dalam CPU (host), kemudian di-copy ke GPU, GPU melakukan suatu modifikasi (atau perhitungan) terhadap array, kemudian di-copy lagi ke CPU. Catatan: kode ini hanya dites dalam mode emulasi, belum dicobakan ke GPU yang sebenarnya.

#include <stdio.h>
#include <stdlib.h>
#include <cuda_runtime.h>
#include <cublas.h>

// Jumlah baris (leading dimension)
#define M 7
// Jumlah kolom
#define N 6
// Makro untk mengubah sistem indeks menjadi one-based
#define IDX2F(i,j,ld) ((((j)-1)*(ld)) + ((i)-1))

int test_vector();
int test_matrix();

int main(int argc, char **argv)
{
    //test_vector();
    test_matrix();
    
    return EXIT_SUCCESS;
}

int test_vector()
{
    int i;
    cublasStatus stat;
    float* d_veca_ptr; // device pointer
    float* veca_ptr; // host pointer

    // Alokasi untuk memori host
    veca_ptr = (float*)malloc(M*sizeof(*veca_ptr));
    if(!veca_ptr){
        printf("Alokasi memori pada host gagal\n");
        return EXIT_FAILURE;
    }

    // Isi vektor
    for(i = 1; i <= M; i++){
        veca_ptr[IDX2F(i,1,M)] = (i-1)*5.f;
    }

    // Tampilkan isi vektor
    printf("\nNilai vektor veca_ptr (sebelum dimodifikasi dalam GPU):\n");
    for(i = 1; i<= M; i++){
        printf("veca_ptr[%d] = %7.5f\n", i, veca_ptr[IDX2F(i,1,M)]);
    }

    // Inisialisasi CUBLAS
    cublasInit();

    // Alokasi memori pada GPU
    stat = cublasAlloc(M, sizeof(*d_veca_ptr), (void**)&d_veca_ptr);
    if(stat != CUBLAS_STATUS_SUCCESS){
        printf("Alokasi memori pada device gagal\n");
        cublasShutdown();
        return EXIT_FAILURE;
    }

    // Copy vektor dari host ke device
    stat = cublasSetVector(M, sizeof(*veca_ptr), veca_ptr, 1, d_veca_ptr, 1);
    if(stat != CUBLAS_STATUS_SUCCESS){
        printf("Download data ke GPU gagal\n");
        cublasFree(d_veca_ptr);
        cublasShutdown();
        return EXIT_FAILURE;
    }

    // Modifikasi d_veca_ptr
    printf("\n");
    for(i = 1; i <= M; i++){
        d_veca_ptr[IDX2F(i,1,M)] = 1.f;
    }

    // Copy vektor yang sudah dimodifikasi pada device ke host
    stat = cublasGetVector(M, sizeof(*d_veca_ptr), d_veca_ptr, 1, veca_ptr, 1);
    if(stat != CUBLAS_STATUS_SUCCESS){
        printf("Upload data dari host gagal\n");
        cublasFree(d_veca_ptr);
        cublasShutdown();
        return EXIT_FAILURE;
    }

    cublasFree(d_veca_ptr);

    // Tampilkan lagi vektor veca_ptr (yang sudah dimodifikasi dalam GPU)
    printf("\nNilai vektor veca_ptr (setelah dimodifikasi dalam GPU):\n");
    for(i = 1; i<= M; i++){
        printf("veca_ptr[%d] = %7.5f\n", i, veca_ptr[IDX2F(i,1,M)]);
    }

    free(veca_ptr);
    cublasShutdown();
}

int test_matrix()
{
    int i, j;
    cublasStatus stat;
    float* d_matA_ptr;
    float* matA_ptr;

    // Alokasi memori untuk matA_ptr
    matA_ptr = (float*)malloc(M*N*sizeof(*matA_ptr));
    if(!matA_ptr){
        printf("Alokasi memori pada host gagal\n");
        return EXIT_FAILURE;
    }

    // Inisialisasi nilai matriks matA_ptr
    for(j = 1; j <= N; j++){
        for(i = 1; i <= M; i++){
            matA_ptr[IDX2F(i,j,M)] = (i-1)*M + j;
        }
    }

    // Tampilkan matriks
    printf("\nSebelum modifikasi oleh GPU:\n");
    for(j = 1; j <= N; j++){
        for(i = 1; i <= M; i++){
            printf("matA_ptr[%d,%d] = %7.2f\n", i, j, matA_ptr[IDX2F(i,j,M)]);
        }
    }
    
    // Inisialisasi CUBLAS
    cublasInit();

    // Alokasi memori pada device
    stat = cublasAlloc(M*N, sizeof(*matA_ptr), (void**)&d_matA_ptr);
    if(stat != CUBLAS_STATUS_SUCCESS){
        printf("Alokasi memori pada device gagal\n");
        cublasShutdown();
        return EXIT_FAILURE;
    }

    // Copy matriks dari host ke device
    stat = cublasSetMatrix(M,N,sizeof(*matA_ptr), matA_ptr, M, d_matA_ptr, M);
    if(stat != CUBLAS_STATUS_SUCCESS){
        printf("Download data ke device gagal\n");
        cublasFree(d_matA_ptr);
        cublasShutdown();
        return EXIT_FAILURE;
    }

    // Modifikasi matriks, atau panggil fungsi CUBLAS yang diinginkan
    for(j = 1; j <= N; j++){
        for(i = 1; i <= M; i++){
            d_matA_ptr[IDX2F(i,j,M)] = i + j;
        }
    }

    stat = cublasGetMatrix(M, N, sizeof(*matA_ptr), d_matA_ptr, M, matA_ptr, M);
    if(stat != CUBLAS_STATUS_SUCCESS){
        printf("Upload data dari device gagal\n");
        cublasFree(d_matA_ptr);
        cublasShutdown();
        return EXIT_FAILURE;
    }

    cublasFree(d_matA_ptr);
    cublasShutdown();

    // Tampilkan hasil modifikasi
    printf("\nSetelah modifikasi oleh GPU:\n");
    for(j = 1; j <= N; j++){
        for(i = 1; i <= M; i++){
            printf("matA_ptr[%d,%d] = %7.2f\n", i, j, d_matA_ptr[IDX2F(i,j,M)]);
        }
    }

    free(matA_ptr);
    cublasShutdown();
}

Kompilasi dan link dengan menggunakan mode emulasi (jika tersedia GPU kompilasi langsung tanpa option -deviceemu dan link dengan -lcublas, jika tidak maka Anda [and the author included] termasuk orang yang belum beruntung dalam hal ini):

$ nvcc -deviceemu test_cublas_helper.c -lcublasemu

Hasil program untuk pemanggilan subrutin test_vector():

Nilai vektor veca_ptr (sebelum dimodifikasi dalam GPU):
veca_ptr[1] = 0.00000
veca_ptr[2] = 5.00000
veca_ptr[3] = 10.00000
veca_ptr[4] = 15.00000
veca_ptr[5] = 20.00000
veca_ptr[6] = 25.00000
veca_ptr[7] = 30.00000


Nilai vektor veca_ptr (setelah dimodifikasi dalam GPU):
veca_ptr[1] = 1.00000
veca_ptr[2] = 1.00000
veca_ptr[3] = 1.00000
veca_ptr[4] = 1.00000
veca_ptr[5] = 1.00000
veca_ptr[6] = 1.00000
veca_ptr[7] = 1.00000

Contoh hasil program untuk pemanggilan subrutin test_matrix():

Sebelum modifikasi oleh GPU:
matA_ptr[1,1] =    1.00
matA_ptr[2,1] =    8.00
... // sebagian output tidak ditampilkan
matA_ptr[6,6] =   41.00
matA_ptr[7,6] =   48.00

Setelah modifikasi oleh GPU:
matA_ptr[1,1] =    2.00
matA_ptr[2,1] =    3.00
matA_ptr[3,1] =    4.00
... // sebagian output tidak ditampilkan
matA_ptr[6,6] =   12.00
matA_ptr[7,6] =   13.00

Referensi

  • NVIDIA¬†Corporation. NVidia CUBLAS Library Documentation. 2009

Kontributor: Fadjar