【问题标题】:Add scalar to vector in BLAS (cuBLAS/CUDA)在 BLAS (cuBLAS/CUDA) 中将标量添加到向量
【发布时间】:2012-12-12 15:07:14
【问题描述】:

我不知道我是否只是忽略了一些明显的东西,但是尽管在谷歌上进行了适当的搜索,但我看不到使用 BLAS 操作简单地将标量添加到向量(或矩阵)的方法。我正在尝试在 cuBLAS/CUDA 中执行此操作,因此我将采取任何方式在该框架内完成此操作。 BLAS 有 <t>scal 用于标量乘法 (cublas<t>scal),但加法的模拟量在哪里?! IE。类似于 GSL gsl_vector_add_constant 的东西。我错过了什么?

【问题讨论】:

  • 这个操作很简单,为什么不自己写内核呢?是否与使用您选择的编译器而不是 nvcc 有关?
  • @Pavan 哈!确实为什么不呢?我想这就是我可能会做的。我刚刚开始使用 CUDA/cuBLAS,我可能会被困在一个心理“盒子”中。

标签: c cuda add blas cublas


【解决方案1】:

可能完成您所要求的唯一方法是将axpy 与一个大小相同的单位向量应用您要添加的常数缩放。

所以操作就变成了X <- X + alpha * I,相当于在X中的每一个条目上加上alpha


编辑:

从 cmets 看来,您似乎预见到在为 SAXPY 调用创建单位向量时会遇到一些困难。一种方法是使用 memset 调用在设备上设置单位向量的值,如下所示:

#include "cuda.h"
#include "cuda_runtime_api.h"
#include "cublas_v2.h"
#include <iostream>

int main(void)
{

    const int N = 10;
    const size_t sz = sizeof(float) * size_t(N);
    float *A, *I;

    float Ah[N] = { 0., 1., 2., 3., 4., 5., 6., 7., 8., 9. };

    cudaMalloc((void **)&A, sz);
    cudaMemcpy(A, &Ah[0], sz, cudaMemcpyHostToDevice);

    // this creates a bit pattern for a single precision unity value
    // and uses 32-bit memset from the driver API to set the values in the
    // vector.
    const float one = 1.0f;
    const int* one_bits = reinterpret_cast<const int*>(&one);
    cudaMalloc((void **)&I, sz);
    cuMemsetD32(CUdeviceptr(I), *one_bits, N);

    cublasHandle_t h;
    cublasCreate(&h);

    const float alpha = 5.0f;
    cublasSaxpy(h, N, &alpha, I, 1, A, 1);

    cudaMemcpy(&Ah[0], A, sz, cudaMemcpyDeviceToHost);

    for(int i=0; i<N; i++) {
        std::cout << i << " " << Ah[i] << std::endl;
    }

    cublasDestroy(h);
    cudaDeviceReset();

    return 0;
}

请注意,我直接使用 CUDA 运行时 API 为 CUBLAS 向量分配和复制内存,而不是使用 CUBLAS 辅助函数(无论如何,它们只是运行时 API 调用的非常薄的包装器)。 “棘手”部分是制作位模式并使用驱动程序 API memset 函数来设置数组的每个 32 位字。

您同样可以使用来自推力库的几行模板代码来完成整个工作,或者只需编写自己的内核,这可能就像

template<typename T>
__global__
void vector_add_constant( T * vector, const T scalar, int N)
{
    int tidx = threadIdx.x + blockIdx.x*blockDim.x;
    int stride = blockDim.x * gridDim.x;

    for(; tidx < N; tidx += stride) {
        vector[tidx] += scalar;
    }
}

[免责声明:此内核是在浏览器中编写的,未经测试。使用风险自负]

【讨论】:

  • 对——但是 a) 分配一个完整的向量只是为了做到这一点?我完全困惑为什么这应该是必要的。 b) 如何在 cuBLAS 中分配单位向量?如果我没记错的话,那里没有随机访问。将其分配在 CPU 上,然后将其移至 GPU 会增加更多不必要的工作。
  • @MattPhillips:从技术上讲,您所询问的操作在数学上是未定义的,这就是为什么没有 BLAS 操作的原因。您的替代方法是编写一个内核来执行操作(出于效率原因,最好将多个操作“融合”到一个内核中)。如果您可以等待几个小时,我将使用 CUBLAS 示例编辑我的答案,包括单位向量分配
  • " 您所询问的操作在数学上是未定义的" 什么?标量加法?我完全不明白你的意思,但如果是这样,那为什么它在 GSL 中?
  • 我不打算写一篇关于 cmets 中向量空间属性的讲座,但在线性代数中确实没有向向量添加标量这样的事情。许多计算机实现允许“广播”来做到这一点,但他们真正做的是我建议的 axpy 操作的数学等价物,即使他们在执行操作时没有明确地形成单位向量或矩阵。
  • 我知道向量空间的定义,从概念上讲,我想要的可以用你描述的 axpy 来考虑,但就如何实现而言> 它,实际上创建单位向量等是坦率的愚蠢。 x86 程序集有addmul,我很难相信nVidia 程序集也没有add,在这种情况下,实现应该是迭代和添加的问题。但是怎么可能没有高级功能来做到这一点......
【解决方案2】:

四个选项,从最好到最差排列:

  • 在不同的库中找到您需要的函数
  • 自己实现需要的功能
  • 分配并初始化一个常量向量,将其与*axpy 一起使用。
  • 尽管 BLAS 正式不支持步幅为零,但某些实现将步幅为 0 的向量视为您想要的“标量”。也许 cuBLAS 可以。但是,依赖于此是一个非常糟糕的主意(糟糕到我强烈认为不提它),因为 BLAS 不支持这种行为;您的代码将不可移植,甚至可能会被库的未来版本破坏,除非 nvidia 提供比 BLAS 更强大的 API 保证。

【讨论】:

  • 哪些实现将 0 步长向量视为标量输入?这听起来像是一个非常有用的功能
  • macOS 和 iOS 附带的 BLAS 将零步幅向量视为标量。根据输入向量或矩阵的大小,0 步幅向量也非常快。
  • 与 CUDA 无关,但英特尔 MKL 似乎支持 0 步长向量。
  • CUBLAS 过去不支持 0-stride。我将其报告为错误,但我不知道它是否已修复。如果有帮助,这是我的暴露错误的代码:github.com/ParRes/Kernels/blob/default/Cxx11/…
猜你喜欢
  • 2013-09-02
  • 2011-02-14
  • 1970-01-01
  • 2012-04-27
  • 1970-01-01
  • 2013-09-29
  • 2019-10-07
  • 1970-01-01
  • 2019-05-03
相关资源
最近更新 更多