4

我目前正在使用 GASS 的 CUDA.NET 库。我需要在一个 CPU 线程中初始化 cuda 数组(实际上是 cublas 向量,但这并不重要)并在其他 CPU 线程中使用它们。但是包含所有初始化数组和加载函数的 CUDA 上下文只能附加到一个 CPU 线程。

有一种称为上下文迁移 API 的机制可以从一个线程中分离上下文并将其附加到另一个线程。但我不知道如何在 CUDA.NET 中正确使用它。

我试过这样的事情:

class Program
{
    private static float[] vector1, vector2;
    private static CUDA cuda;
    private static CUBLAS cublas;

    private static CUdeviceptr ptr;

    static void Main(string[] args)
    {
        cuda = new CUDA(false);
        cublas = new CUBLAS(cuda);
        cuda.Init();
        cuda.CreateContext(0);
        AllocateVectors();
        cuda.DetachContext();
        CUcontext context = cuda.PopCurrentContext();
        GetVectorFromDeviceAsync(context);
    }

    private static void AllocateVectors()
    {
        vector1 = new float[]{1f, 2f, 3f, 4f, 5f};
        ptr = cublas.Allocate(vector1.Length, sizeof (float));
        cublas.SetVector(vector1, ptr);

        vector2 = new float[5];
    }


    private static void GetVectorFromDevice(object objContext)
    {
        CUcontext localContext = (CUcontext) objContext;
        cuda.PushCurrentContext(localContext);
        cuda.AttachContext(localContext);

        //change vector somehow
        vector1[0] = -1;
        //copy changed vector to device
        cublas.SetVector(vector1, ptr);
        cublas.GetVector(ptr, vector2);
        CUDADriver.cuCtxPopCurrent(ref localContext);
    }

    private static void GetVectorFromDeviceAsync(CUcontext cUcontext)
    {
        Thread thread = new Thread(GetVectorFromDevice);
        thread.IsBackground = false;
        thread.Start(cUcontext);
    }
}

但是尝试将更改的向量复制到设备时执行失败,因为未附加上下文。其他原因不太可能,因为它在单线程模式下工作正常。有什么想法可以让它工作吗?

4

2 回答 2

2

我仍然没有找到解决这个问题的方法,但我确实想出了一个解决方法。关键是在一个 CPU 线程中执行所有需要处理 CUDA 的函数。例如,您可以这样做:

class Program
{
    private static float[] vector1, vector2;
    private static CUDA cuda;
    private static CUBLAS cublas;

    private static CUdeviceptr ptr;

    private static readonly AutoResetEvent autoResetEvent = new AutoResetEvent(false);

    static void Main()
    {
        cuda = new CUDA(true);
        cublas = new CUBLAS(cuda);

        //allocate vector on cuda device in main thread
        CudaManager.CallMethod(AllocateVectors);

        //changing first vector from other thread
        Thread changeThread = new Thread(ChangeVectorOnDevice_ThreadRun) { IsBackground = false };
        changeThread.Start();

        //wait for changeThread to finish
        autoResetEvent.WaitOne();

        //getting vector from device in another one thread
        Thread getThread = new Thread(GetVectorFromDevice_ThreadRun) { IsBackground = false };
        getThread.Start();

        //wait for getThread to finish
        autoResetEvent.WaitOne();

        Console.WriteLine("({0}, {1}, {2}, {3}, {4})", vector2[0], vector2[1], vector2[2], vector2[3], vector2[4]);

        Console.ReadKey(true);
    }

    private static void AllocateVectors()
    {
        vector1 = new[] { 1f, 2f, 3f, 4f, 5f };
        vector2 = new float[5];
        //allocate memory and copy first vector to device
        ptr = cublas.Allocate(vector1.Length, sizeof(float));
        cublas.SetVector(vector1, ptr);

    }

    private static void GetVectorFromDevice()
    {
        cublas.GetVector(ptr, vector2);
    }

    private static void ChangeVectorOnDevice()
    {
        //changing vector and copying it to device
        vector1 = new[] { -1f, -2f, -3f, -4f, -5f };
        cublas.SetVector(vector1, ptr);
    }

    private static void ChangeVectorOnDevice_ThreadRun()
    {
        CudaManager.CallMethod(ChangeVectorOnDevice);
        //releasing main thread
        autoResetEvent.Set();
    }

    private static void GetVectorFromDevice_ThreadRun()
    {
        CudaManager.CallMethod(GetVectorFromDevice);
        //releasing main thread
        autoResetEvent.Set();
    }
}

public static class CudaManager
{
    public static Action WorkMethod { get; private set; }

    private static readonly AutoResetEvent actionRecived = new AutoResetEvent(false);
    private static readonly AutoResetEvent callbackEvent = new AutoResetEvent(false);

    private static readonly object mutext = new object();
    private static bool isCudaThreadRunning;

    private static void ThreadRun()
    {
        //waiting for work method to execute
        while (actionRecived.WaitOne())
        {
            //invoking recived method
            WorkMethod.Invoke();
            //releasing caller thread
            callbackEvent.Set();
        }
    }

    static CudaManager()
    {
        Run();
    }

    public static void Run()
    {
        if (!isCudaThreadRunning)
        {
            Thread thread = new Thread(ThreadRun);
            thread.IsBackground = true;
            thread.Start();
            isCudaThreadRunning = true;
        }
    }

    public static void CallMethod(Action method)
    {
        lock (mutext)
        {
            WorkMethod = method;
            //releasing ThreadRun method
            actionRecived.Set();
            //blocking caller thread untill delegate invokation is complete
            callbackEvent.WaitOne();
        }
    }
}

我希望它会帮助某人。

于 2010-05-01T10:30:06.323 回答
1

查看 GASS 文档中的 CUDAContextSynchronizer 类。

于 2010-07-08T21:48:31.443 回答