Как правильно передать аргументы ядру с помощью драйвера Cuda api cuLaunchKernel?

Я создаю некоторые буферы устройств, которые я пытаюсь передать простому ядру, используя API-интерфейс драйвера cuda. Я создаю три буфера устройств и сохраняю их в std::vector.

std::vector<void *> kernel_arguments;

std::vector<float> a = {2};
std::vector<float> b = {3};

for (auto &input : {a, b}) {
    CUdeviceptr ptr;
    cuMemAlloc(&ptr, input.size()*sizeof(float));
    cuMemcpyHtoD(ptr, input.data(), input.size()*sizeof(float));
    kernel_arguments.push_back(reinterpret_cast<void *> (&ptr));
}

std::vector<float> c(1);

for (auto &output : {c}) {
    CUdeviceptr ptr;
    cuMemAlloc(&ptr, output.size()*sizeof(float));
    kernel_arguments.push_back(reinterpret_cast<void *> (&ptr));
}

CUresult result = cuLaunchKernel(function, 1, 1, 1,
                                 1024, 1, 1, 0, stream,
                                 kernel_arguments.data(), NULL)
const char *error;
cuGetErrorString(result, &error);
std::cout << result << " " << error << std::end;
result = cuStreamSynchronize(stream);
cuGetErrorString(result, &error);
std::cout << result << " " << error << std::end;

Функция ядра представляет собой простое ядро ​​сложения с тремя аргументами.

__global__ void add_kernel(
    float *i_1,
    float *i_2,
    float *o_3) {
    const size_t index = blockIdx.x*blockDim.x + threadIdx.x;
    if (index < 1) {
        printf("index %d\n", index);
        printf("%p\n", i_1);
        printf("%f\n", *i_1);
        const float r_1 = i_1[index];
        printf("%p\n", i_2);
        printf("%f\n", *i_2);
        const float r_2 = i_2[index];
        const float r_3 = r_1 + r_2;
        o_3[index] = r_3;
    }
}

Запустив это, я получаю результат.

0 no error
index 0
0x14cf4c400200
3.000000
0x14cf4c400200
3.000000
700 an illegal memory access was encountered

Почему я получаю одно и то же значение указателя для первого и второго аргументов и почему кажется, что буфер моего второго устройства оказывается в первом аргументе?

Я уже просмотрел их и сопоставил, что они делают, только используя вектор C++ вместо массива C.

user1139069 10.01.2023 20:43
Laravel с Turbo JS
Laravel с Turbo JS
Turbo - это библиотека JavaScript для упрощения создания быстрых и высокоинтерактивных веб-приложений. Она работает с помощью техники под названием...
Типы ввода HTML: Лучшие практики и советы
Типы ввода HTML: Лучшие практики и советы
HTML, или HyperText Markup Language , является стандартным языком разметки, используемым для создания веб-страниц. Типы ввода HTML - это различные...
Аутсорсинг разработки PHP для индивидуальных веб-решений
Аутсорсинг разработки PHP для индивидуальных веб-решений
Услуги PHP-разработки могут быть экономически эффективным решением для компаний, которые ищут высококачественные услуги веб-разработки по доступным...
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
Слишком много useState? Давайте useReducer!
Слишком много useState? Давайте useReducer!
Современный фронтенд похож на старую добрую веб-разработку, но с одной загвоздкой: страница в браузере так же сложна, как и бэкенд.
Узнайте, как использовать теги &lt;ul&gt; и &lt;li&gt; для создания неупорядоченных списков в HTML
Узнайте, как использовать теги <ul> и <li> для создания неупорядоченных списков в HTML
HTML предоставляет множество тегов для структурирования и организации содержимого веб-страницы. Одним из наиболее часто используемых тегов для...
0
1
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Эта методология работает, когда вы возвращаете обратно значение, расположенное в стеке, но не когда вы возвращаете обратно адрес расположения в стеке — это не меняется от одной итерации цикла for к другой:

for (auto &input : {a, b}) {
    CUdeviceptr ptr;  // a stack variable
    cuMemAlloc(&ptr, input.size()*sizeof(float));
    cuMemcpyHtoD(ptr, input.data(), input.size()*sizeof(float));
    kernel_arguments.push_back(reinterpret_cast<void *> (&ptr));  //**
}
// ptr is out of scope here

Это объясняет, почему первый и второй параметры ссылаются на ваш второй входной аргумент ядра (i_2, 3).

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

Вот пример (измененный из vectorAddDrv примера кода) с изменением вашего цикла распределения (то есть перезапись значения стека на каждой итерации), чтобы решить эту проблему:

$ cat vectorAddDrv.cpp
// Includes
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <cstring>
#include <cuda.h>

// includes, project
#include <helper_cuda_drvapi.h>
#include <helper_functions.h>

// includes, CUDA
#include <builtin_types.h>
#include <vector>

using namespace std;

// Variables
CUdevice cuDevice;
CUcontext cuContext;
CUmodule cuModule;
CUfunction vecAdd_kernel;

//define input fatbin file
#ifndef FATBIN_FILE
#define FATBIN_FILE "vectorAdd_kernel64.fatbin"
#endif

// Host code
int main(int argc, char **argv)
{
    // Initialize
    checkCudaErrors(cuInit(0));

    cuDevice = findCudaDeviceDRV(argc, (const char **)argv);
    // Create context
    checkCudaErrors(cuCtxCreate(&cuContext, 0, cuDevice));

    // first search for the module path before we load the results
    string module_path;

    std::ostringstream fatbin;

    if (!findFatbinPath(FATBIN_FILE, module_path, argv, fatbin))
    {
        exit(EXIT_FAILURE);
    }
    else
    {
        printf("> initCUDA loading module: <%s>\n", module_path.c_str());
    }

    if (!fatbin.str().size())
    {
        printf("fatbin file empty. exiting..\n");
        exit(EXIT_FAILURE);
    }

    // Create module from binary file (FATBIN)
    checkCudaErrors(cuModuleLoadData(&cuModule, fatbin.str().c_str()));

    // Get function handle from module
    checkCudaErrors(cuModuleGetFunction(&vecAdd_kernel, cuModule, "VecAdd_kernel"));
    // your code, modified
    std::vector<void *> kernel_arguments;

    std::vector<float> a = {2};
    std::vector<float> b = {3};

    for (auto &input : {a, b}) {
        CUdeviceptr *ptr = new CUdeviceptr;
        cuMemAlloc(ptr, input.size()*sizeof(float));
        cuMemcpyHtoD(*ptr, input.data(), input.size()*sizeof(float));
        kernel_arguments.push_back(ptr);
    }

    std::vector<float> c(1);

    for (auto &output : {c}) {
        CUdeviceptr *ptr = new CUdeviceptr;
        cuMemAlloc(ptr, output.size()*sizeof(float));
        kernel_arguments.push_back(ptr);
    }

    CUresult result = cuLaunchKernel(vecAdd_kernel, 1, 1, 1,
                                 1024, 1, 1, 0, NULL,
                                 kernel_arguments.data(), NULL);
    const char *error;
    cuGetErrorString(result, &error);
    std::cout << result << " " << error << std::endl;
    checkCudaErrors(cuCtxSynchronize());
    cuGetErrorString(result, &error);
    std::cout << result << " " << error << std::endl;
    for (auto &c : kernel_arguments) cuMemFree(*(reinterpret_cast<CUdeviceptr *>(c)));  // this works since all of the kernel arguments in this case happen to be CUdeviceptr

    exit(EXIT_SUCCESS);
}
$ nvcc -I/usr/local/cuda/samples/common/inc  -o test vectorAddDrv.cpp  -lcuda
$ compute-sanitizer ./test
========= COMPUTE-SANITIZER
> Using CUDA Device [0]: Tesla V100-PCIE-32GB
> findModulePath found file at <./vectorAdd_kernel64.fatbin>
> initCUDA loading module: <./vectorAdd_kernel64.fatbin>
0 no error
index 0
0x7f8023c00000
2.000000
0x7f8023c00200
3.000000
0 no error
========= ERROR SUMMARY: 0 errors
$

Другие вопросы по теме