2014-01-13 4 views
1

다음 프로그램은 cuSPARSE을 사용하여 조밀 한 변환을 스파 스 변환으로 테스트합니다. 처음 몇 줄의 출력에 쓰레기가 생깁니다. 그러나 (1)으로 표시된 줄 다음에 (2)으로 표시된 줄을 이동하면 프로그램이 정상적으로 작동합니다. 누군가 그 이유가 무엇인지 말해 줄 수 있습니까?cuSPARSE를 사용하여 dense-to-sparse 및 sparse-dense 변환

편집 : 프레젠테이션 명확하게하기 위해, 나는 thrust와 프로그램을 다시 썼다, 동일한 문제가 지속.

편집 : 로버트에 의해 제안, 나는 thrust없이 다시 버전으로 변경 및 API 레벨 에러 체크 코드를 추가했다.

#include <iostream> 
#include <cusparse_v2.h> 

using std::cerr; 
using std::cout; 
using std::endl; 

#define WRAP(x) do {x} while (0) 
#define CHKcusparse(x) WRAP(          \ 
    cusparseStatus_t err = (x);          \ 
    if (err != CUSPARSE_STATUS_SUCCESS) {        \ 
    cerr << "Cusparse Error #" << int(err) << "\"TODO\" at Line " \ 
     << __LINE__ << " of " << __FILE__ << ": " << #x << endl; \ 
    exit(1);              \ 
    }                 \ 
) 
#define CHKcuda(x) WRAP(           \ 
    cudaError_t err = (x);            \ 
    if (err != cudaSuccess) {           \ 
    cerr << "Cuda Error #" << int(err) << ", \""      \ 
     << cudaGetErrorString(err) << "\" at Line " << __LINE__  \ 
     << " of " << __FILE__ << ": " << #x << endl;    \ 
    exit(1);               \ 
    }                 \ 
) 
#define ALLOC(X, T, N) do {       \ 
    h##X = (T*) malloc(sizeof(T) * (N));     \ 
    CHKcuda(cudaMalloc((void**)&d##X, sizeof(T) * (N))); \ 
} while(0) 

int main() { 
    srand(100); 

    cusparseHandle_t g_cusparse_handle; 
    CHKcusparse(cusparseCreate(&g_cusparse_handle)); 

    const int n = 100, in_degree = 10; 
    int nnz = n * in_degree, nn = n * n; 

    int *dnnz, *dridx, *dcols; 
    int *hnnz, *hridx, *hcols; 
    float *dvals, *dmat; 
    float *hvals, *hmat; 

    // (1) The number of non-zeros in each column. 
    ALLOC(nnz, int, n); 

    // The dense matrix. 
    ALLOC(mat, float, nn); 

    // The values in sparse matrix. 
    ALLOC(vals, float, nnz); 

    // (2) The row indices of the sparse matrix. 
    ALLOC(ridx, int, nnz); 

    // The column offsets of the sparse matrix. 
    ALLOC(cols, int, n+1); 

    // Fill and copy dense matrix and number of non-zeros. 
    for (int i = 0; i < nn; i++) {hmat[i] = rand();} 
    for (int i = 0; i < n; i++) {hnnz[i] = in_degree;} 
    CHKcuda(cudaMemcpyAsync(dnnz, hnnz, sizeof(int) * n, cudaMemcpyHostToDevice)); 
    CHKcuda(cudaMemcpyAsync(dmat, hmat, sizeof(float) * nn, cudaMemcpyHostToDevice)); 
    CHKcuda(cudaDeviceSynchronize()); 

    // Perform dense to CSC format 
    cusparseMatDescr_t cspMatDesc; 
    CHKcusparse(cusparseCreateMatDescr(&cspMatDesc)); 
    CHKcusparse(cusparseSdense2csc(
     g_cusparse_handle, n, n, cspMatDesc, dmat, n, 
     dnnz, dvals, dridx, dcols 
)); 

    // Copy row indices back. 
    CHKcuda(cudaMemcpyAsync(hridx, dridx, sizeof(int) * nnz, cudaMemcpyDeviceToHost)); 
    CHKcuda(cudaDeviceSynchronize()); 
    CHKcusparse(cusparseDestroyMatDescr(cspMatDesc)); 

    // Display row indices. 
    for (int i = 0; i < n; i++) { 
    for (int j = 0; j < in_degree; j++) { 
     std::cout << hridx[i * in_degree + j] << ", "; 
    } 
    std::cout << std::endl; 
    } 

    CHKcuda(cudaFree(dnnz)); 
    CHKcuda(cudaFree(dvals)); 
    CHKcuda(cudaFree(dridx)); 
    CHKcuda(cudaFree(dcols)); 
    CHKcuda(cudaFree(dmat)); 
    free(hnnz); 
    free(hmat); 
    free(hvals); 
    free(hridx); 
    free(hcols); 
    return 0; 
} 
+0

오류하지? 다른 사용자에게 도움을 요청하기 전에 CUDA 및 cusparse API에 대한 기본 API 수준 오류 검사를 활용해야합니다. 당신은 라인 1 또는 2의 위치에 관계없이 오류를 반환하는 cusparse 함수가 있습니다. 열당 nnz는 10이지만 사실 열의 수가 0이 아닌 요소가 10 개 이상인 밀집 행렬을 초기화한다고 선언하면 밀도가 높은 스파 스 변환이 폭발하기 때문입니다. Cusparse는 컬럼 당 nnz를 사전 계산하는 기능을 제공합니다. 그러나 당신의 경우에는'in_degree'를 10 대신에 100으로 설정함으로써 오류를 제거 할 수 있습니다. –

+0

상기시켜 주셔서 감사합니다. 질문을하기 위해 큰 코드베이스에서 추출했습니다. 나는이 모든 전화가 성공적으로 돌아 왔는지 시험했다. 조밀 한 행렬에 관해서는, n에 의해 조밀 한 행렬을 만들고, 그 다음 그것을 10 개의 0이 아닌 원소를 갖는 각 열을 가진 n × n의 크기가 작은 희소 행렬로 변환하려고합니다. 이것이 설정이라면 변환 기능을 올바르게 호출하는 방법이 맞습니까? 아니면 내가 잘못 이해 한 것이 있습니까? – shaoyl85

+0

@RobertCrovella 죄송합니다, 귀하의 요점을 볼 수 내 고밀도 매트릭스가 각 열에 10 개 이상의 0 아닌 요소가 있다면, 열 10 nnz로 변환 전화를 할 수 없다는 뜻인가요? 변환이 자동으로 가장 0이 아닌 요소 10 개를 자동으로 선택하지 않습니까? – shaoyl85

답변

1

기본 문제는 내부적으로 일치하지 않는 데이터를 dense-to-sparse routine에 전달하는 것입니다. 당신은 컬럼 당 100이 아닌 원소를 가진 조밀 한 행렬을 전달하고 있지만, 컬럼 당 10 개의 비 0 원소 만 있다고 cusparse에게 말하고 있습니다.

cuda-memcheck으로 코드를 실행하면 cusparse에서 오류가 발생하는 것을 볼 수 있습니다. 이 코드에 대한

, 당신은 일반적인 경우를 들어 100

in_degree 변수를 변경하여 문제를 해결할 수, cusparse 제대로 열 당 비제 요소의 수를 채우기 위해 a convenient routine을 제공합니다.

+0

그래서 내 진짜 질문은 각 열에서 가장 큰 절대 값을 갖는 k 요소를 효율적으로 선택하고 나머지를 0으로 설정하는 것이다. 너는 어떤 생각을 가지고 있니? 나는 병렬로 여러 k-selection의 문제로 귀결된다고 생각한다. – shaoyl85

+0

마음에 오는 유일한 사항은 각 열 (key = element 절대 값, value = 요소)에서 [sort-by-key] (http://thrust.github.io/doc/group__sorting.html#ga2bb765aeef19f6a04ca8b8ba11efff24)를 수행하는 것입니다. 인덱스)를 계산 한 다음 상위 k 개의 원래 요소를 값 (요소 인덱스)으로 채워서 0 열로 다시 채 웁니다. 나는 당신이 더 나은 아이디어를 위해 새로운 질문으로 그것을 묻는 것이 좋습니다. –

0

Robert Crovella가 이미 밑줄 쳐서, 밀도가 높은 것으로부터 희소로 이어지는 것은 cusparse<t>nnz()cusparse<t>dense2csr() 루틴으로 cuSPARSE을 사용하여 효과적으로 수행 할 수 있습니다. 그 반대의 경우는 cusparse<t>csr2dense() 루틴으로 수행 할 수 있습니다. 아래에는 CSR 형식으로 cuSPARSE을 사용하여 밀도가 지나치게 높거나 그 반대 인 방법을 보여주는 완전하게 작동하는 예제가 있습니다. 확인

cuSparseUtilities.cuh

#ifndef CUSPARSEUTILITIES_CUH 
#define CUSPARSEUTILITIES_CUH 

#include "cusparse_v2.h" 

void setUpDescriptor(cusparseMatDescr_t &, cusparseMatrixType_t, cusparseIndexBase_t); 
void dense2SparseD(const double * __restrict__ d_A_dense, int **d_nnzPerVector, double **d_A, 
    int **d_A_RowIndices, int **d_A_ColIndices, int &nnz, cusparseMatDescr_t descrA, 
    const cusparseHandle_t handle, const int Nrows, const int Ncols); 

#endif 

cuSparseUtilities.cu

#include "cuSparseUtilities.cuh" 
#include "Utilities.cuh" 

/*****************************/ 
/* SETUP DESCRIPTOR FUNCTION */ 
/*****************************/ 
void setUpDescriptor(cusparseMatDescr_t &descrA, cusparseMatrixType_t matrixType, cusparseIndexBase_t indexBase) { 
    cusparseSafeCall(cusparseCreateMatDescr(&descrA)); 
    cusparseSafeCall(cusparseSetMatType(descrA, matrixType)); 
    cusparseSafeCall(cusparseSetMatIndexBase(descrA, indexBase)); 
} 

/********************************************************/ 
/* DENSE TO SPARSE CONVERSION FOR REAL DOUBLE PRECISION */ 
/********************************************************/ 
void dense2SparseD(const double * __restrict__ d_A_dense, int **d_nnzPerVector, double **d_A, 
        int **d_A_RowIndices, int **d_A_ColIndices, int &nnz, cusparseMatDescr_t descrA, 
        const cusparseHandle_t handle, const int Nrows, const int Ncols) { 

    const int lda = Nrows;      // --- Leading dimension of dense matrix 

    gpuErrchk(cudaMalloc(&d_nnzPerVector[0], Nrows * sizeof(int))); 

    // --- Compute the number of nonzero elements per row and the total number of nonzero elements in the dense d_A_dense 
    cusparseSafeCall(cusparseDnnz(handle, CUSPARSE_DIRECTION_ROW, Nrows, Ncols, descrA, d_A_dense, lda, d_nnzPerVector[0], &nnz)); 

    // --- Device side sparse matrix 
    gpuErrchk(cudaMalloc(&d_A[0], nnz * sizeof(double))); 
    gpuErrchk(cudaMalloc(&d_A_RowIndices[0], (Nrows + 1) * sizeof(int))); 
    gpuErrchk(cudaMalloc(&d_A_ColIndices[0], nnz * sizeof(int))); 

    cusparseSafeCall(cusparseDdense2csr(handle, Nrows, Ncols, descrA, d_A_dense, lda, d_nnzPerVector[0], d_A[0], d_A_RowIndices[0], d_A_ColIndices[0])); 

} 

kernel.cu

#include "cuda_runtime.h" 
#include "device_launch_parameters.h" 

#include <stdio.h> 

#include <cusparse_v2.h> 

#include "cuSparseUtilities.cuh" 
#include "Utilities.cuh" 

/********/ 
/* MAIN */ 
/********/ 
int main() { 

    cusparseHandle_t handle; 

    // --- Initialize cuSPARSE 
    cusparseSafeCall(cusparseCreate(&handle)); 

    cusparseMatDescr_t descrA = 0; 

    /**************************/ 
    /* SETTING UP THE PROBLEM */ 
    /**************************/ 
    const int Nrows = 5;      // --- Number of rows 
    const int Ncols = 4;      // --- Number of columns 
    const int N = Nrows; 

    // --- Host side dense matrix 
    double *h_A_dense = (double*)malloc(Nrows * Ncols * sizeof(*h_A_dense)); 

    // --- Column-major storage 
    h_A_dense[ 0] = 0.4612f; h_A_dense[ 5] = -0.0006f; h_A_dense[10] = 1.3f;  h_A_dense[15] = 0.0f; 
    h_A_dense[ 1] = 0.0f;  h_A_dense[ 6] = 1.443f;  h_A_dense[11] = 0.0f;  h_A_dense[16] = 0.0f; 
    h_A_dense[ 2] = -0.0006f; h_A_dense[ 7] = 0.4640f; h_A_dense[12] = 0.0723f; h_A_dense[17] = 0.0f; 
    h_A_dense[ 3] = 0.3566f; h_A_dense[ 8] = 0.0723f; h_A_dense[13] = 0.7543f; h_A_dense[18] = 0.0f; 
    h_A_dense[ 4] = 0.f;  h_A_dense[ 9] = 0.0f;  h_A_dense[14] = 0.0f;  h_A_dense[19] = 0.1f; 

    // --- Create device array and copy host array to it 
    double *d_A_dense; gpuErrchk(cudaMalloc(&d_A_dense, Nrows * Ncols * sizeof(double))); 
    gpuErrchk(cudaMemcpy(d_A_dense, h_A_dense, Nrows * Ncols * sizeof(*d_A_dense), cudaMemcpyHostToDevice)); 

    /*******************************/ 
    /* FROM DENSE TO SPARSE MATRIX */ 
    /*******************************/ 
    // --- Descriptor for sparse matrix A 
    setUpDescriptor(descrA, CUSPARSE_MATRIX_TYPE_GENERAL, CUSPARSE_INDEX_BASE_ONE); 

    int nnz = 0;        // --- Number of nonzero elements in dense matrix 
    int *d_nnzPerVector;      // --- Device side number of nonzero elements per row 

    double *d_A;        // --- Sparse matrix values - array of size nnz 
    int *d_A_RowIndices;      // --- "Row indices" 
    int *d_A_ColIndices;      // --- "Column indices" 

    dense2SparseD(d_A_dense, &d_nnzPerVector, &d_A, &d_A_RowIndices, &d_A_ColIndices, nnz, descrA, handle, Nrows, Ncols); 

    /*******************************************************/ 
    /* CHECKING THE RESULTS FOR DENSE TO SPARSE CONVERSION */ 
    /*******************************************************/ 
    // --- Host side number of nonzero elements per row 
    int *h_nnzPerVector = (int *)malloc(Nrows * sizeof(int)); 
    gpuErrchk(cudaMemcpy(h_nnzPerVector, d_nnzPerVector, Nrows * sizeof(int), cudaMemcpyDeviceToHost)); 

    printf("Number of nonzero elements in dense matrix = %i\n\n", nnz); 
    for (int i = 0; i < Nrows; ++i) printf("Number of nonzero elements in row %i = %i \n", i, h_nnzPerVector[i]); 
    printf("\n"); 

    // --- Host side sparse matrix 
    double *h_A = (double *)malloc(nnz * sizeof(double)); 
    int *h_A_RowIndices = (int *)malloc((Nrows + 1) * sizeof(int)); 
    int *h_A_ColIndices = (int *)malloc(nnz * sizeof(int)); 
    gpuErrchk(cudaMemcpy(h_A, d_A, nnz * sizeof(double), cudaMemcpyDeviceToHost)); 
    gpuErrchk(cudaMemcpy(h_A_RowIndices, d_A_RowIndices, (Nrows + 1) * sizeof(int), cudaMemcpyDeviceToHost)); 
    gpuErrchk(cudaMemcpy(h_A_ColIndices, d_A_ColIndices, nnz * sizeof(int), cudaMemcpyDeviceToHost)); 

    printf("\nOriginal matrix in CSR format\n\n"); 
    for (int i = 0; i < nnz; ++i) printf("A[%i] = %f\n", i, h_A[i]); printf("\n"); 

    printf("\n"); 
    for (int i = 0; i < (Nrows + 1); ++i) printf("h_A_RowIndices[%i] = %i \n", i, h_A_RowIndices[i]); printf("\n"); 

    for (int i = 0; i < nnz; ++i) printf("h_A_ColIndices[%i] = %i \n", i, h_A_ColIndices[i]); 

    /*******************************/ 
    /* FROM SPARSE TO DENSE MATRIX */ 
    /*******************************/ 
    double *d_A_denseReconstructed; gpuErrchk(cudaMalloc(&d_A_denseReconstructed, Nrows * Ncols * sizeof(double))); 
    cusparseSafeCall(cusparseDcsr2dense(handle, Nrows, Ncols, descrA, d_A, d_A_RowIndices, d_A_ColIndices, 
             d_A_denseReconstructed, Nrows)); 

    /*******************************************************/ 
    /* CHECKING THE RESULTS FOR SPARSE TO DENSE CONVERSION */ 
    /*******************************************************/ 
    double *h_A_denseReconstructed = (double *)malloc(Nrows * Ncols * sizeof(double)); 
    gpuErrchk(cudaMemcpy(h_A_denseReconstructed, d_A_denseReconstructed, Nrows * Ncols * sizeof(double), cudaMemcpyDeviceToHost)); 

    printf("\nReconstructed dense matrix \n"); 
    for (int m = 0; m < Nrows; m++) { 
     for (int n = 0; n < Ncols; n++) 
      printf("%f\t", h_A_denseReconstructed[n * Nrows + m]); 
     printf("\n"); 
    } 

    return 0; 
} 
관련 문제