使用CUBLAS查找最大值和最小值

5

我遇到了一个问题,不明白为什么使用CUBLAS查找一组double型数据的最大值和最小值的函数无法正常工作。

代码如下:

void findMaxAndMinGPU(double* values, int* max_idx, int* min_idx, int n)
{
    double* d_values;
    cublasHandle_t handle;
    cublasStatus_t stat;
    safecall( cudaMalloc((void**) &d_values, sizeof(double) * n), "cudaMalloc     (d_values) in findMaxAndMinGPU");
    safecall( cudaMemcpy(d_values, values, sizeof(double) * n, cudaMemcpyHostToDevice), "cudaMemcpy (h_values > d_values) in findMaxAndMinGPU");
    cublasCreate(&handle);

    stat = cublasIdamax(handle, n, d_values, sizeof(double), max_idx);
    if (stat != CUBLAS_STATUS_SUCCESS)
        printf("Max failed\n");

    stat = cublasIdamin(handle, n, d_values, sizeof(double), min_idx);
    if (stat != CUBLAS_STATUS_SUCCESS)
        printf("min failed\n");

    cudaFree(d_values);
    cublasDestroy(handle);
}

在这里,values是要搜索的数值。max_idx和min_idx是找到的数值在values中的索引。 CUBLAS调用的结果似乎很随机,并输出错误的索引。

有没有人能够给我一个好的答案来解决我的问题?我现在有点难过 :(


尝试将max_idxmin_idx初始化为int类型,而不是int *类型,并通过引用&max_idx传递给cublas。 - Vanwaril
2个回答

7

您传递给和函数的参数有误。在BLAS第一级调用中,incx参数应该是输入的步幅,以字为单位,而不是字节。因此,我怀疑您想要的是:

stat = cublasIdamax(handle, n, d_values, 1, max_idx);
if (stat != CUBLAS_STATUS_SUCCESS)
    printf("Max failed\n");

stat = cublasIdamin(handle, n, d_values, 1, min_idx);
if (stat != CUBLAS_STATUS_SUCCESS)
    printf("min failed\n");

通过使用sizeof(double),你告诉程序使用步长为8,这将导致调用超出输入数组的分配存储并进入未初始化的内存。我猜测你实际上在d_values中有一个步长为1。

编辑:以下是一个完整可运行的示例,它可以正确地工作。请注意,我将代码切换为单精度,因为我目前没有双精度能力的硬件:

#include <cuda_runtime.h>
#include <cublas_v2.h>
#include <cstdio>
#include <cstdlib>
#include <sys/time.h>


typedef float Real;

void findMaxAndMinGPU(Real* values, int* max_idx, int* min_idx, int n)
{
    Real* d_values;
    cublasHandle_t handle;
    cublasStatus_t stat;
    cudaMalloc((void**) &d_values, sizeof(Real) * n);
    cudaMemcpy(d_values, values, sizeof(Real) * n, cudaMemcpyHostToDevice);
    cublasCreate(&handle);

    stat = cublasIsamax(handle, n, d_values, 1, max_idx);
    if (stat != CUBLAS_STATUS_SUCCESS)
        printf("Max failed\n");

    stat = cublasIsamin(handle, n, d_values, 1, min_idx);
    if (stat != CUBLAS_STATUS_SUCCESS)
        printf("min failed\n");

    cudaFree(d_values);
    cublasDestroy(handle);
}

int main(void)
{
    const int vmax=1000, nvals=10000;

    float vals[nvals];
    srand ( time(NULL) );
    for(int j=0; j<nvals; j++) {
       vals[j] = float(rand() % vmax);
    }

    int minIdx, maxIdx;
    findMaxAndMinGPU(vals, &maxIdx, &minIdx, nvals);

    int cmin = 0, cmax=0;
    for(int i=1; i<nvals; i++) {
        cmin = (vals[i] < vals[cmin]) ? i : cmin;
        cmax = (vals[i] > vals[cmax]) ? i : cmax;
    }

    fprintf(stdout, "%d %d %d %d\n", minIdx, cmin, maxIdx, cmax);

    return 0;
}

当编译并运行时,会得到如下结果:
$ g++ -I/usr/local/cuda/include -L/usr/local/cuda/lib cublastest.cc -lcudart -lcublas
$ ./a.out
273 272 85 84

请注意,CUBLAS遵循FORTRAN约定并使用1索引,而不是零索引,这就是为什么CUBLAS版本和CPU版本之间存在1的差异。


不幸的是,这并没有解决我的问题。仍然返回看起来随机的结果。 - ssnielsen
@ssnielsen:当您运行我在答案中添加的完整重现案例时会发生什么? - talonmies
它完美地运行。我没有注意到1索引以及CUBLAS在数字的绝对值上工作并忽略数字是否有符号的事实。 - ssnielsen

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接