Как стать автором
Обновить

Комментарии 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?
В голову пришла маленькая идейка сразу: возможно быстрее делать свап между указателями на эти два буффера?
Спасибо!
Отличная идея, так оно, в общем-то и происходит при расчётах на GPU здесь, на CPU здесь и на высоком уровне здесь

Сделайте симулятор муравейника с ИИ. Чтобы можно было наблюдать за тем как муравьи строят и охотятся

Считаю, если в муравейнике нельзя будет грабить корованы, то всё напрасно :-)

Звучит как идея для стартапа в GameDev :)
Честно говоря, не очень понятно почему здесь такая сложность в алгоритме.
Проблема в расчете количества соседей я так понимаю. А почему просто нельзя так сделать — представить матрицу как 0 и 1. Далее чтобы подсчитать есть ли сосед справа для каждой точки, просто сместим матрицу влево. Аналогично для всех остальных направлений. Получаем 8 матриц — сдвинутая исходная матрица во всевозможных направлениях. Далее суммируем все эти матрицы и смотрим что получается. В зависимости от значения в каждой клетке получаем расклад в следующий момент. Вроде все матричные операции быстро выполняются на GPU.
Идею я понимаю и она кажется разумной и простой. С точки зрения реализации навскидку встаёт вопрос как быстро, просто и эффективно подготовить 8 матриц размера 32'768^2 / 8 байт каждая?

Эти матрицы получаются урезанием и добавлением всего либо одного столбца либо одной строчки либо того и другого. Если поместилась в памяти одна такая матрица, то если ещё 8 поместятся вроде не должно быть проблемой с точки зрения сложности. Копирование целого массива и добавление пустых строк и столбцов. Будут размеры в 10 раз меньше условно у поля, но оно такое же громадное. Для некоторых направлений, допустим для определения соседа снизу, надо только матрицу сдвинуть вниз, в Си например, я так понимаю, можно теже ячейки памяти использовать, только указатель начала массива переместить на размер первой строки. Ну добавить ещё в конце нули в матрице. Для других направлений сложнее. Но в целом это можно обобщить. Есть матрица с padding, пустыми краями, и мы сдвигаем центр матрицы по 8 направлениям. Как потом суммировать такие матрицы. Это прям вопрос из машинного обучения. Там же есть операция свертки с ядром. Здесь просто ядро громадное.

Звучит очень интересно! Пока не приходит на ум как это сделать эффективно, поэтому предлагаю сделать форк на гитхабе и сможем обсудить и сравнить :)

Пока не обещаю насчёт реализации) Вот какая мысль ещё возникла. Можно ведь организовать свертку с равномерным ядром. Эта свертка прямо нативная операция на GPU и библиотеках машинного обучения. Я имею ввиду, что надо исходное изображение свернуть с ядром размера 3*3 состоящего из единиц кроме центра который 0. И никаких 8 матриц делать не надо. Эта свертка уже оптимизирована и производится в разных алгоритмах машинного зрения. Так называемый conv слой.

Нет, ядро не громадное, а 3x3.
Если считать сумму не только соседней, но и значения в клетке, то можно будет разбить на композицию двух свёрток: 3x1 и 1x3.
Не знаю, как будет быстрее, но мне кажется, что проще читать 9 рядом расположенных значений из одной матрицы, чем заниматься суммированием значений из девяти различных матриц.

А зачем так сложно? )
Разве нельзя поместить 9 поверхностей со смещениями и с полупрозрачной текстурой. Где соседи например в R-канале, а текущая в G например. Потом написать простой пороговый фильтр по цвету и нарисовать обратно на текстуре?
Ух ты! Звучит так что можно. Полагаю, мне не пришло на ум в силу небольшого опыта работы с видеокартами.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории