Пример закрепленной памяти JCuda

JCuda + GEForce Gt640 Вопрос:

Я пытаюсь уменьшить задержку, связанную с копированием памяти с устройства на хост после того, как результат вычислен графическим процессором. Делая простую программу Vector Add, я обнаружил, что большая часть задержки действительно копирует буфер результатов обратно на сторону хоста. Задержка передачи исходных буферов на стороне устройства незначительна ~.30 мс, а копирование результата обратно составляет порядка 20 мс.

Я провел исследование и обнаружил, что лучшей альтернативой копированию результатов является использование закрепленной памяти. Из того, что я узнал, эта память выделяется на стороне хоста, но ядро ​​будет иметь прямой доступ к ней через pci-e, что, в свою очередь, даст более высокую скорость, чем копирование результата после массового вычисления. Я использую следующий пример, но результаты не дают того, что я ожидаю.

Ядро: {Простой пример, чтобы проиллюстрировать точку, запуск 1 блока 1 потока только}

extern "C"
__global__ void add(int* test)
{
    test[0]=1; test[1]=2; test[2]=3; test[3]=4; test[4]=5;
}

Джава:

import java.io.*;
import jcuda.*;
import jcuda.runtime.*;
import jcuda.driver.*;

import static jcuda.runtime.cudaMemcpyKind.*;
import static jcuda.driver.JCudaDriver.*;

public class JCudaTest
{
    public static void main(String args[])
    {
        // Initialize the driver and create a context for the first device.
        cuInit(0);
        CUdevice device = new CUdevice();
        cuDeviceGet(device, 0);
        CUcontext context = new CUcontext();
        cuCtxCreate(context, 0, device);

        // Load the ptx file.
        CUmodule module = new CUmodule();
        JCudaDriver.cuModuleLoad(module, "JCudaKernel.ptx");

        // Obtain a function pointer to the kernel function.
        CUfunction function = new CUfunction();
        JCudaDriver.cuModuleGetFunction(function, module, "add");

        Pointer P = new Pointer();
        JCudaDriver.cuMemAllocHost(P, 5*Sizeof.INT);

        Pointer kernelParameters = Pointer.to(P);
        // Call the kernel function with 1 block, 1 thread:
        JCudaDriver.cuLaunchKernel(function, 1, 1, 1, 1, 1, 1, 0, null, kernelParameters, null);
        int [] T = new int[5];
        JCuda.cudaMemcpy(Pointer.to(T), P, 5*Sizeof.INT, cudaMemcpyHostToHost);

         // Print the results:
         for(int i=0; i<5; i++)
                System.out.println(T[i]);
    }
}

1.) Постройте ядро: root@NVS295-CUDA:~/JCUDA/MySamples# nvcc -ptx JCudaKernel.cu root@NVS295-CUDA:~/JCUDA/MySamples# ls -lrt | grep ptx -rw-r- r-- 1 root root 3295 27 марта 17:46 JCudaKernel.ptx

2.) Сборка Java: root @ NVS295-CUDA: ~ / JCUDA / MySamples # javac -cp "../JCuda-All-0.5.0-bin-linux-x86/*:." JCudaTest.java

3.) Запустите код: root@NVS295-CUDA:~/JCUDA/MySamples# java -cp "../JCuda-All-0.5.0-bin-linux-x86/*:." JCudaTest 0 0 0 0 0

Ожидается: 1 2 3 4 5

Примечание: я использую JCuda0.5.0 для x86, если это имеет значение.

Пожалуйста, дайте мне знать, что я делаю не так, и спасибо заранее: Илир

1 ответ

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

По общему признанию, документация здесь вводит в заблуждение:

cuMemAllocHost

Выделяет байтовые байты памяти хоста, которая заблокирована на странице и доступна для устройства...

Это звучит как четкое утверждение. Однако "доступность" здесь не означает, что память может использоваться непосредственно в качестве параметра ядра во всех случаях. Это возможно только на устройствах, поддерживающих унифицированную адресацию. Для всех других устройств необходимо получить указатель устройства, который соответствует выделенному указателю хоста, с помощью cuMemHostGetDevicePointer.

Ключевым моментом памяти хоста с блокировкой страницы является то, что передача данных между хостом и устройством происходит быстрее. Пример использования этой памяти в JCuda можно увидеть в образце JCudaBandwidthTest (это для API времени выполнения, но для API драйвера это работает аналогично).

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

Обратите внимание, что новая функция Unified Memory в CUDA 6 фактически поддерживает то, что вы изначально намеревались сделать: cudaMallocManaged Вы можете выделить память, которая напрямую доступна хосту и устройству (в том смысле, что она может, например, передаваться ядру, записываться устройством и впоследствии считываться хостом без дополнительных усилий). К сожалению, эта концепция не очень хорошо отображается на Java, потому что память по-прежнему управляется CUDA - и эта память не может заменить память, которая, например, используется виртуальной машиной Java для float[] массив или так. Но, по крайней мере, должно быть возможно создать ByteBuffer из памяти, которая была выделена с cudaMallocManaged, так что вы можете получить доступ к этой памяти, например, как FloatBuffer,

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