Я разрабатываю симуляцию в Scala внутри игры. Я бы хотел использовать только неизменные объекты для определения логики игры, даже если она неэффективна. Зачем? Потому что я могу, так и сделаю.Обработка тяжелой нагрузки на GC с помощью классов Scala Case
Теперь я немного подчеркиваю основной игровой цикл с толканием имитации с огромной нагрузкой. В основном у меня есть эти вложенные классы case, и я работаю с .copy(), чтобы определить следующее состояние данного объекта внутри моделирования. Проблема в том, что все эти операции генерируют много объектов без ссылок, поэтому в конце каждого шага я запускаю полный GC. Это плохо для игровых представлений.
Итак, я начал агрессивную оптимизацию: прежде всего теперь шаг за шагом запускается параллельно за игрой, вычисляя «следующее состояние», которое в основном охватывает день в игре. Таким образом, игроку будет показано состояние с текущего дня, в то время как следующий будет вычисляться. Это работает, потому что в основном каждый крупный субъект в игре (города, в основном) предполагается эволюционировать изолированно. Если какой-либо агент (игрок или другой агент AI, путешествующий между городами) свяжется с городом, я буду пересчитывать «следующее состояние» в соответствии с действиями агента. В любом случае это не имеет значения.
Итак, у меня есть эти параллельные сущности, развивающиеся за сценой, и когда закончился день (день определяется как 5 шагов игрока на карте мира) Я использую Await.result(nextWorldState, 5 seconds)
в качестве точки rendez-vous для обновления текущего состояния моделирования. Это не обязательно, как игра должна работать, но я хочу проверить точку rendez-vous, когда остальная часть игры ждет вычисления следующего состояния, потому что это, вероятно, будет необходимо.
Проблема заключается в том, что после этого я разыменовываю множество объектов сразу, и это вызывает сбор GC. Я пробовал некоторые решения, но всегда есть момент, когда я получаю доступ к результату Будущего и заменяю его текущим состоянием, и это всегда вызывает GC.
Предполагая, что я не хочу идти на обычные классы (возможно, буду) и предполагаю, что я не хочу разбивать состояние на несколько частей и обрабатывать их отдельно (я попробую это, но он выглядит неприступным) , есть ли какое-нибудь умное решение этой проблемы?
Вот некоторые из них не столь псевдокод от фактической функции, что справиться с этой логикой:
class WorldSimulationManager(var worldState: WorldState) {
private var nextWorldState: Future[WorldState] =
Future.successful(worldState)
def doImmutableStep(gameMap:GameMap,movedCountToday:Int):Unit = {
nextWorldState =
nextWorldState.flatMap(_.doStep(gameMap))
if (movedCountToday >= Constants.tileMovementsToDay) {
worldState = Await.result(nextWorldState, 5 seconds)
}
}
}
На самом деле создание множества недолговечных неизменяемых объектов в значительной степени является лучшим случаем для современных поколений GC. Еще 10 лет назад GC GC Azul продемонстрировал вспашку через 20 GiByte/s мусора, созданного 700 параллельными нитями на 864-ядерной машине, даже не разбив пота, используя симуляцию Rich Hickey's Ant в Clojure. Современные GC поколения основаны на гипотезе поколений: объекты умирают молодыми, старые объекты не ссылаются на молодые объекты. Это именно то, что делает FP: много короткоживущих объектов, нет ссылок «назад вовремя». И никаких мутаторов. –
@ JörgWMittag 25 МБ/с/ядро было много для этой машины, но теперь вы получаете машины, производящие 1 ГБ/с/ядро. Тем не менее, самый простой способ настроить gc - это снизить нагрузку. –
Ясно, что я буду работать над ограничением количества созданных объектов, но теперь я просто хочу оценить жизнеспособность этого подхода при тяжелых нагрузках. Во всяком случае, я попробовал CMS, и я просмотрел некоторые наблюдения, используя jstat.В результате существует лишь небольшая разница в частоте кадров между CMS и GC по умолчанию, и если нагрузка слишком велика, обе они не могут достичь удовлетворительных характеристик. Тем не менее я много узнал о GC (никогда не должен был оптимизировать его таким образом) и о моем коде. Дело в том, что пока я беру как текущее, так и следующее состояние в памяти, у меня возникнут проблемы. – Chobeat