Как периодически вызывать задачу GPU с помощью Hybridizer - PullRequest
0 голосов
/ 19 марта 2020

Я пытаюсь использовать гибридизатор для вычислений с использованием графического процессора в C#. Я пытался встроить свой код графического процессора в библиотеку. NET, используя гибрид Altime sh. Давайте назовем это GPU_DLL.

Эта библиотека должна использоваться в другом проекте, назовем ее Test_GPU_Caller. Код GPU периодически вызывается с использованием таймера, как показано в следующем коде:

class Program
{

    static GPU_DLL.GPU_DLL gpuDllTest = new GPU_DLL.GPU_DLL();

    static void Main(string[] args)
    {
        Timer tmr = new Timer(100);
        tmr.Elapsed += Tmr_Elapsed;
        tmr.Start();

        while (true) ;
    }
    private static void Tmr_Elapsed(object sender, ElapsedEventArgs e)
    {
        gpuDllTest.Test("GPU_DLL_CUDA.dll");
    }

}

Функция проверки реализована в библиотеке. NET со следующим кодом:

public class GPU_DLL
{
    [EntryPoint("run")]
    public void Run(int N, double[] a, double[] b)
    {
        Parallel.For(0, N, i => { a[i] += b[i]; });
    }

    public void Test(string dllName)
    {
        // 268 MB allocated on device -- should fit in every CUDA compatible GPU
        int N = 1024 * 1024 * 16;
        double[] acuda = new double[N];
        double[] adotnet = new double[N];

        double[] b = new double[N];

        Random rand = new Random();

        //Initialize acuda et adotnet and b by some doubles randoms, acuda and adotnet have same numbers. 
        for (int i = 0; i < N; ++i)
        {
            acuda[i] = rand.NextDouble();
            adotnet[i] = acuda[i];
            b[i] = rand.NextDouble();
        }

        cudaDeviceProp prop;
        cuda.GetDeviceProperties(out prop, 0);
        HybRunner runner = HybRunner.Cuda(dllName).SetDistrib(prop.multiProcessorCount * 16, 128);

        // create a wrapper object to call GPU methods instead of C#
        dynamic wrapped = runner.Wrap(this);

        // run the method on GPU
        wrapped.Run(N, acuda, b);

        // run .Net method
        Run(N, adotnet, b);

        // verify the results
        for (int k = 0; k < N; ++k)
        {
            if (acuda[k] != adotnet[k])
                Console.Out.WriteLine("ERROR !");
        }
        Console.Out.WriteLine("DONE");
        //Thread.Sleep(10000);
    }
}

GPU_DLL был сначала гибридизирован и скомпилирован. Когда я запускаю программу, я получаю сообщение об ошибке: System.ArgumentException: «Un aélement ave c la me clme a déjà été ajouté». (на французском ...)

Кто-нибудь знает, как использовать код GPU, периодически вызываемый с помощью Hybridizer? Пример проекта приветствуется;)

С уважением, Валентин

1 Ответ

0 голосов
/ 20 марта 2020

Похоже, проблема, с которой вы сталкиваетесь, связана с тем, что таймер вызывает функцию во второй раз до завершения первого вызова. Согласно документации, ваша установка таймера будет вызывать функцию обратного вызова таймера каждые 100 миллисекунд, однако она не будет ждать выполнения предыдущего запуска (для ее запуска в моей системе требуется 1,3 секунды для первой итерации и ~ 650 мс для последующих вызовов).

HybRunner не является поточно-ориентированным. Также обратите внимание, что контекст CUDA присоединен к потоку, а для использования другого потока требуется другой контекст CUDA.

Вот альтернативная реализация для решения вашей проблемы с использованием рабочего потока, который выполняет задачи, связанные с графическим процессором:

Сначала объявите компоненты синхронизации:

    static bool workerrunning = false;
    static object started = new object();

    static Queue<object> tasks = new Queue<object>();
    static object watch = new object();

Рабочий поток l oop может быть следующим:

    public static void HybridLoop(object p)
    {
        lock (started)
        {
            workerrunning = true;
            Monitor.Pulse(started);
        }
        while (workerrunning)
        {
            object currenttask = null;
            // get next task
            lock (watch)
            {
                if (tasks.Count == 0)
                {
                    Monitor.Wait(watch);
                }
                if (tasks.Count != 0)
                {
                    currenttask = tasks.Dequeue();
                    // NOTE: here, we illustrate more tasks than doable => clear
                    tasks.Clear();
                }
            }

            if (currenttask != null)
            {
                gpuDllTest.Test("GPU_DLL_CUDA.dll");
            }
        }
    }

Здесь мы очищаем очередь задач, поскольку она заполняется быстрее, чем потребляется.

Код для запуска рабочего потока:

        System.Threading.Thread hybridrunner = new System.Threading.Thread(HybridLoop);
        lock(started)
        {
            hybridrunner.Start();
            Monitor.Wait(started);
        }

Как только рабочий запущен, таймер может быть запущен:

        System.Timers.Timer tmr = new System.Timers.Timer(100);
        tmr.Elapsed += Tmr_Elapsed;
        tmr.Start();

        Console.Out.WriteLine("Runner started - press return to stop");
        var key = Console.Read();

Ваша функция истечения таймера - просто задание задачи:

    private static void Tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        lock (watch)
        {
            tasks.Enqueue(1);
            Monitor.Pulse(watch);
        }
    }

После выполнения бегун может быть остановлен, чтобы избежать запуска хвостового потока:

        workerrunning = false;
        lock (watch)
        {
            tmr.Stop();
            tasks.Enqueue(0);
            Monitor.Pulse(watch);
        }
        Console.Out.WriteLine("Runner stopping");

        hybridrunner.Join();

        Console.Out.WriteLine("Runner stopped");

Пример журнала выполнения:

DONE
Execution took 1026 milliseconds
DONE
Execution took 642 milliseconds
DONE
Execution took 614 milliseconds
Runner started - press return to stop
DONE
DONE
DONE

Runner stopping
DONE
Runner stopped

В качестве последнего примечания вы можете выполнить некоторую инициализацию, такую ​​как HybRunner.Cuda (), только один раз, а затем выполнять другие задачи в том же рабочем потоке.

...