Добавьте скаляр к вектору в BLAS (cuBLAS/CUDA)

Я не знаю, пропускаю ли я что-то очевидное, но, несмотря на то, что я гуглюсь вокруг, я не вижу способа просто добавить скаляр к вектору (или матрице), используя операции BLAS. Я пытаюсь сделать это в cuBLAS/CUDA, поэтому я воспользуюсь любым способом для достижения этой цели. БЛАС имеет <t>scal для скалярного умножения (cublas<t>scal) а где аналог для сложения?! Т.е. что-то похожее на GSL gsl_vector_add_constant, Что мне не хватает?

2 ответа

Решение

Вероятно, единственный способ сделать то, что вы просите, это применить axpy с единичным вектором того же размера, масштабируемого по константе, которую вы хотите добавить.

Таким образом, операция становится X <- X + alpha * I, что эквивалентно добавлению alpha к каждой записи в X,


РЕДАКТИРОВАТЬ:

Судя по комментариям, вы предвидите некоторые трудности в создании вектора единиц для вызова 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;
}

Обратите внимание, что здесь я выделил и скопировал память для векторов CUBLAS, используя непосредственно API-интерфейс времени выполнения CUDA, вместо того, чтобы использовать вспомогательные функции CUBLAS (которые в любом случае являются лишь очень тонкими обертками для вызовов API-интерфейса времени выполнения). "Хитрая" часть создает битовый шаблон и использует функцию memset API драйвера для установки каждого 32-битного слова массива.

Вы также можете сделать все это с помощью пары строк шаблонного кода из библиотеки Thrust или просто написать свое собственное ядро, которое может быть таким же простым, как

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;
    }
}

[Disclaimer: это ядро ​​было написано в браузере и не проверено. Использовать как собственный риск]

Четыре варианта, от лучших до худших:

  • Найдите нужную вам функцию в другой библиотеке
  • Реализуйте нужную вам функцию
  • Выделите и инициализируйте постоянный вектор, используйте его с *axpy,
  • Хотя шаги BLAS формально не поддерживаются BLAS, некоторые реализации рассматривают вектор с шагом 0 как "скаляр" в том смысле, в котором вы хотите. Может быть, cuBLAS делает. Тем не менее, в зависимости от этого это действительно плохая идея (настолько плохая, что я решительно не упомянул ее), так как это поведение не поддерживается BLAS; ваш код не будет переносимым, и он может даже сломаться будущими версиями библиотеки, если nvidia не дает более надежную гарантию API, чем BLAS.
Другие вопросы по тегам