ядро cuda для игры жизни Конвея - PullRequest
1 голос
/ 14 декабря 2010

Я пытаюсь вычислить количество переходов, которые будут сделаны в прогоне GOL Конвея для матрицы pxq для n итераций.Например, дана 1 итерация с начальным состоянием 1 мигатель (как показано ниже).было бы 5 переходов (2 рождения, 1 выживание, 2 смерти от неполного населения).Я уже получил эту работу, но я хотел бы преобразовать эту логику для запуска с использованием CUDA.Ниже я хочу портировать на CUDA.

alt text код:

    static void gol() // call this iterations x's
    {
        int[] tempGrid = new int[rows * cols]; // grid holds init conditions
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                tempGrid[i * cols + j] = grid[i * cols + j];
            }
        }

        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                int numNeighbors = neighbors(i, j); // finds # of neighbors

                if (grid[i * cols + j] == 1 && numNeighbors > 3)
                {
                    tempGrid[i * cols + j] = 0;
                    overcrowding++;
                }
                else if (grid[i * cols + j] == 1 && numNeighbors < 2)
                {
                    tempGrid[i * cols + j] = 0;
                    underpopulation++;
                }
                else if (grid[i * cols + j] == 1 && numNeighbors > 1)
                {
                    tempGrid[i * cols + j] = 1;
                    survival++;
                }
                else if (grid[i * cols + j] == 0 && numNeighbors == 3)
                {
                    tempGrid[i * cols + j] = 1;
                    birth++;
                }
            }
        }

        grid = tempGrid;
    }

Ответы [ 2 ]

3 голосов
/ 14 декабря 2010

Основным замедлением будет доступ к основной памяти. Поэтому я бы посоветовал вам выбрать большой размер блока нитей в зависимости от имеющегося у вас оборудования. 256 (16x16) - хороший выбор для кросс-аппаратной совместимости. Каждый из этих потоковых блоков будет рассчитывать результаты для немного меньшей секции платы - если вы использовали 16x16, они будут вычислять результаты для секции 14x14 платы, поскольку существует граница из одного элемента. (Причиной использования блока 16x16 для вычисления фрагмента 14x14 вместо фрагмента 16x16 является объединение чтения памяти.)

Разделите доску на (скажем) 14x14 кусков; это ваша сетка (организована так, как вы считаете нужным, но, скорее всего, что-то вроде board_width / 14, board_height / 14.

В ядрах каждый поток загружает свой элемент в общую память. Тогда синхронизируй. Затем средние элементы 14x14 рассчитывают новое значение (используя значения, хранящиеся в общей памяти) и записывают его обратно в глобальную память. Использование разделяемой памяти помогает минимизировать глобальное чтение и запись. Это также является причиной того, чтобы размер блока вашего потока был как можно большим - ребра и углы «теряют» глобальный доступ к памяти, поскольку извлекаемые там значения используются только 1 или 3 раза, а не 9 раз.

0 голосов
/ 14 декабря 2010

Вот один из способов, которым вы могли бы продолжить:

  1. Каждый поток выполняет вычисления для 1 элемента сетки
  2. Каждый поток сначала загружает один элемент из основной сетки в общую память
  3. Потоки на краю блока нитей также должны загружать граничные элементы
  4. Каждый поток может затем рассчитать свое выживание на основе содержимого общей памяти
  5. Каждый поток затем записывает свой результат обратно в основную память
...