Как использовать CUDA tex1DFetch с cudaTextureObject_t? - PullRequest
0 голосов
/ 26 марта 2019

Я работал с ссылками на текстуры, когда заметил, что они устарели, я попытался обновить свою тестовую функцию для работы с «новыми» объектами текстуры без привязки с помощью tex1Dfetch, но не смог получить те же результаты.

В настоящее время я изучаю использование текстурной памяти для ускорения моей ахорадовой реализации; Мне удалось заставить tex1D() работать со ссылками на текстуры, однако я заметил, что они устарели, и решил вместо этого использовать объекты текстуры.

Я получаю очень странное поведение с ядрами, когда пытаюсь использовать результаты каким-либо образом; Я могу сделать results[tidx] = tidx; без проблем, но results[tidx] = temp + 1; только когда-либо возвращает значение temp, а не temp * 3 или любой другой числовой тест, включающий temp.

Я не вижу логической причины для такого поведения, и примеры документации выглядят достаточно похожими, так что я не вижу, где я ошибся.

Я уже прочитал неправильное поведение CUDA tex1Dfetch () и Новый объект текстуры CUDA - получение неверных данных в 2D-случае, но ни одно из них не связано с моей проблемой.

На всякий случай, если это имеет значение; Я использую CUDA версии 10.0, V10.0.130 с Nvidia GTX 980ti.

#include <iostream>

__global__ void test(cudaTextureObject_t tex ,int* results){
    int tidx = threadIdx.y * blockDim.x + threadIdx.x;
    unsigned temp = tex1Dfetch<unsigned>(tex, threadIdx.x);
    results[tidx] = temp * 3;
}

int main(){
    int *host_arr;
    const int host_arr_size = 8;

    // Create and populate host array
    std::cout << "Host:" << std::endl;
    cudaMallocHost(&host_arr, host_arr_size*sizeof(int));
    for (int i = 0; i < host_arr_size; ++i){
        host_arr[i] = i * 2;
        std::cout << host_arr[i] << std::endl;
    }

    // Create resource description
    struct cudaResourceDesc resDesc;
    resDesc.resType = cudaResourceTypeLinear;
    resDesc.res.linear.devPtr = &host_arr;
    resDesc.res.linear.sizeInBytes = host_arr_size*sizeof(unsigned);
    resDesc.res.linear.desc = cudaCreateChannelDesc<unsigned>();
    // Create texture description
    struct cudaTextureDesc texDesc;
    texDesc.readMode = cudaReadModeElementType;
    // Create texture
    cudaTextureObject_t tex;
    cudaCreateTextureObject(&tex, &resDesc, &texDesc, NULL);

    // Allocate results array
    int * result_arr;
    cudaMalloc(&result_arr, host_arr_size*sizeof(unsigned));

    // launch test kernel
    test<<<1, host_arr_size>>>(tex, result_arr);

    // fetch results
    std::cout << "Device:" << std::endl;
    cudaMemcpy(host_arr, result_arr, host_arr_size*sizeof(unsigned), cudaMemcpyDeviceToHost);
    // print results
    for (int i = 0; i < host_arr_size; ++i){
        std::cout << host_arr[i] << std::endl;
    }

    // Tidy Up
    cudaDestroyTextureObject(tex);
    cudaFreeHost(host_arr);
    cudaFree(result_arr);
}

Я ожидал, что вышеприведенное будет работать аналогично приведенному ниже (что работает):


texture<int, 1, cudaReadModeElementType> tex_ref;
cudaArray* cuda_array;

__global__ void test(int* results){
    const int tidx = threadIdx.x;
    results[tidx] = tex1D(tex_ref, tidx) * 3;
}

int main(){
    int *host_arr;
    int host_arr_size = 8;

    // Create and populate host array
    cudaMallocHost((void**)&host_arr, host_arr_size * sizeof(int));
    for (int i = 0; i < host_arr_size; ++i){
        host_arr[i] = i * 2;
        std::cout << host_arr[i] << std::endl;
    }

    // bind to texture
    cudaChannelFormatDesc cuDesc = cudaCreateChannelDesc <int >();
    cudaMallocArray(&cuda_array, &cuDesc, host_arr_size);
    cudaMemcpyToArray(cuda_array, 0, 0, host_arr , host_arr_size * sizeof(int), cudaMemcpyHostToDevice);
    cudaBindTextureToArray(tex_ref , cuda_array);
    // Allocate results array
    int * result_arr;
    cudaMalloc((void**)&result_arr, host_arr_size*sizeof(int));

    // launch kernel
    test<<<1, host_arr_size>>>(result_arr);

    // fetch results
    cudaMemcpy(host_arr, result_arr, host_arr_size * sizeof(int), cudaMemcpyDeviceToHost);
    // print results
    for (int i = 0; i < host_arr_size; ++i){
        std::cout << host_arr[i] << std::endl;
    }

    // Tidy Up
    cudaUnbindTexture(tex_ref);
    cudaFreeHost(host_arr);
    cudaFreeArray(cuda_array);
    cudaFree(result_arr);
}

Ожидаемые результаты:

Host:
0
2
4
6
8
10
12
14
Device:
0
6
12
18
24
30
36
42

Фактические результаты:

Host:
0
2
4
6
8
10
12
14
Device:
0
2
4
6
8
10
12
14

Кто-нибудь знает, что на земле идет не так?

1 Ответ

2 голосов
/ 26 марта 2019

Вызовы функций API CUDA возвращают коды ошибок.Вы хотите проверить эти коды ошибок.Особенно, когда что-то явно идет не так где-то

Этот же массив используется для хранения данных начального массива, а также для получения результата от устройства.Ваш запуск ядра завершается неудачно с ошибкой неправильного адреса, потому что у вас нет действительного объекта текстуры.У вас нет действительного объекта текстуры, потому что создание вашего объекта текстуры не удалось.Первый вызов API сразу после запуска ядра - cudaMemcpy(), чтобы вернуть результаты.Так как во время запуска ядра произошла ошибка, cudaMemcpy() завершится с ошибкой, возвращая самую последнюю ошибку вместо выполнения копирования.В результате содержимое вашего буфера host_arr остается неизменным, и вы просто в конечном итоге снова отображаете исходные входные данные.

Резонанс, почему не удалось создать объект текстуры, объяснен в документации (выделено мной):

Если для cudaResourceDesc :: resType установлено значение cudaResourceTypeLinear, для cudaResourceDesc :: res :: linear :: devPtr должно быть установлено значение действительный указатель устройства , который выровнен с cudaDeviceProp :: textureAlignment.[…]

Объект текстуры не может ссылаться на память хоста.Проблема в вашем коде заключается в следующем:

resDesc.res.linear.devPtr = &host_arr;

Вам необходимо выделить буфер в децитивной памяти, например, используя cudaMalloc(), скопировать туда свои данные и создать объект текстуры, который ссылается на этот буфер устройства..

Кроме того, ваш texDesc не инициализирован должным образом.В вашем случае достаточно просто инициализировать его нулями:

struct cudaTextureDesc texDesc = {};
...