Комментарии 18
Игровое поле всегда квадратное и его сторона всегда степени 2, в частности затем, чтобы без остатка осуществлять деление на 8
24 не является степенью двойки, но, тем не менее, делится без остатка на 8. И таких чисел много
P.S. А, только в частности!
По поводу превосходства GPU: я когда-то пытался делать неоптимизированную реализацию на CUDA, и она оказалась сильно медленнее, чем я ожидал (медленнее вашей версии). Потом я наткнулся на эту статью, в которой утверждается, что ключ к оптимизации — расположение данных в GPU-памяти правильного типа (в идеале, в регистровой памяти).
Их производительность — 0.16 секунд на 1024 итераций на поле 16K на 16K на видеокарте GeForce GTX TITAN X. Эта разница из-за того, что видеокарты несопоставимы по производительности, или Вашу версию еще можно ускорить, если применить аналогичные трюки?
Я не знаком с Cuda, но изучив их статью и погулив, понял о чём речь, поправьте если я не прав.
Cuda даёт в руки программиста управление над L1-кэшем видеокарты и это очень интересная возможность. Что касается Metal, то такая возможность мне не попадалась, поэтому, видимо приходится полагаться на встроенные оптимизации и эффективную работу кэша. Соответственно такая оптимизация не применима, по крайней мере пока.
В то же время важно упомянуть, что Metal предоставляет разные режимы работы памяти, а именно:
- private — память доступна только для GPU
- managed — память доступна для CPU и GPU и требует явной синхронизации
- shared — память доступна для CPU и GPU и не требует явной синхронизации
В моей реализации используется shared, так как на мобильных устройствах видеокарты используют одну и ту же с процессором память. То же касается интегрированных видеокарт в указанных ноутбуках.
Таким образом, при тестировании игры на более мощных неинтегрированных видеокартах, имеет смысл выбрать режим managed и добавить явную синхронизацию во избежание избыточного обмена данными между памятью GPU и общей памятью.
Не сильно в код всматривался, но в статье подметил 1 момент:
«По завершении вычислений достаточно сделать swap буферов и следующий шаг игры готов.»
Как у вас реализован swap?
В голову пришла маленькая идейка сразу: возможно быстрее делать свап между указателями на эти два буффера?
Сделайте симулятор муравейника с ИИ. Чтобы можно было наблюдать за тем как муравьи строят и охотятся
Считаю, если в муравейнике нельзя будет грабить корованы, то всё напрасно :-)
https://habr.com/ru/post/443252/ — муравьи и пчелы с памятью
Проблема в расчете количества соседей я так понимаю. А почему просто нельзя так сделать — представить матрицу как 0 и 1. Далее чтобы подсчитать есть ли сосед справа для каждой точки, просто сместим матрицу влево. Аналогично для всех остальных направлений. Получаем 8 матриц — сдвинутая исходная матрица во всевозможных направлениях. Далее суммируем все эти матрицы и смотрим что получается. В зависимости от значения в каждой клетке получаем расклад в следующий момент. Вроде все матричные операции быстро выполняются на GPU.
Эти матрицы получаются урезанием и добавлением всего либо одного столбца либо одной строчки либо того и другого. Если поместилась в памяти одна такая матрица, то если ещё 8 поместятся вроде не должно быть проблемой с точки зрения сложности. Копирование целого массива и добавление пустых строк и столбцов. Будут размеры в 10 раз меньше условно у поля, но оно такое же громадное. Для некоторых направлений, допустим для определения соседа снизу, надо только матрицу сдвинуть вниз, в Си например, я так понимаю, можно теже ячейки памяти использовать, только указатель начала массива переместить на размер первой строки. Ну добавить ещё в конце нули в матрице. Для других направлений сложнее. Но в целом это можно обобщить. Есть матрица с padding, пустыми краями, и мы сдвигаем центр матрицы по 8 направлениям. Как потом суммировать такие матрицы. Это прям вопрос из машинного обучения. Там же есть операция свертки с ядром. Здесь просто ядро громадное.
del
Звучит очень интересно! Пока не приходит на ум как это сделать эффективно, поэтому предлагаю сделать форк на гитхабе и сможем обсудить и сравнить :)
Нет, ядро не громадное, а 3x3.
Если считать сумму не только соседней, но и значения в клетке, то можно будет разбить на композицию двух свёрток: 3x1 и 1x3.
Не знаю, как будет быстрее, но мне кажется, что проще читать 9 рядом расположенных значений из одной матрицы, чем заниматься суммированием значений из девяти различных матриц.
Разве нельзя поместить 9 поверхностей со смещениями и с полупрозрачной текстурой. Где соседи например в R-канале, а текущая в G например. Потом написать простой пороговый фильтр по цвету и нарисовать обратно на текстуре?
Game of Life с битовой магией, многопоточностью и на GPU