使用Scala案例分类处理GC上的重负载 [英] Handling heavy load on the GC with Scala Case Classes

查看:149
本文介绍了使用Scala案例分类处理GC上的重负载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在Scala内部开发一款模拟游戏。我只想使用不可变对象来定义游戏逻辑,即使它效率低下。为什么?因为我可以,所以我会的。

现在我强调一下主要的游戏循环,推动仿真带来巨大的负载。基本上我有这些嵌套的case类,我使用.copy()来操作来定义仿真中给定实体的以下状态。问题在于所有这些操作都会生成大量未引用的对象,因此在每一步的最后,我都会触发Full GC。这对游戏性能不利。

因此我开始了一些积极的优化:首先现在仿真步骤在游戏后面并行运行,计算出下一个状态基本上涵盖了游戏中的一天。所以玩家将从当天开始显示状态,而下一个正在计算。这是有效的,因为基本上游戏中的每个主要实体(城市,基本上)都被假定为孤立进化。如果某个代理人(在城市间旅行的玩家或其他人工智能代理)将与城市接触,我将根据代理人的操作重新计算下一个状态。这与现在无关。



因此,我有这些平行实体在幕后演变,当一天结束时(一天被定义为玩家的5个步骤世界地图)我使用 Await.result(nextWorldState,5秒)作为rendez-vous点来更新模拟的当前状态。这不一定是游戏应该如何运行,但是当游戏的其余部分等待下一个状态被计算时,我想测试一个rendez-vous点,因为这可能是必需的。



我的问题是,在此之后,我一次解引用很多对象,并触发GC采集。我尝试了一些解决方案,但是当访问Future的结果并用它替换当前状态时总会有一点,这总是触发GC。



假设我不想去普通类(可能我会),并假设我不想将这个状态分成多个部分并处理它们分开(我会尝试这个,但它看起来不可取),是否有任何巧妙的解决方案来解决这个问题?



下面是一些非实际的伪代码,处理这个逻辑:

  class WorldSimulationManager(var worldState:WorldState){

private var nextWorldState:Future [WorldState] =
Future.successful(worldState)

$ b $ def doImmutableStep(gameMap:GameMap,movedCountToday:Int):Unit = {

nextWorldState =
nextWorldState.flatMap(_。doStep(gameMap))

if(movedCountToday> = Constants.tileMovementsToDay){
worldState = Await.result(nextWorldState,5秒)





解决方案

减少pai完整的GC中,我建议使用G1或CMS而不是平行收集器,并增加年轻的空间以减少提升到终身空间的对象的数量,但没有什么比创造更少的工作成为首要。



你创建的垃圾越多,你做的工作就越多,gc清理对象的工作就越多。用更多的CPU更快地创建对象不会让GC变得更加痛苦。


I'm developing a simulation in Scala inside a game. I would like to use only immutable objects to define the game logic even if it's inefficient. Why? Because I can, so I will.

Now I'm stressing a bit the main game loop with pushing the simulation with a huge load. Basically I have these nested case classes and I operate with .copy() to define the following state of a given entity inside the simulation. The problem is that all these operations generate a lot of unreferenced objects and so at the end of every step, I trigger a Full GC. This is bad for the game performances.

So I started some aggressive optimization: first of all now the simulation step is running in parallel behind the game, computing a "next state" that basically covers a day in the game. So the player will be shown the state from the current day while the next one is being computed. This works because basically every major entity in the game (cities, basically) is assumed to evolve in isolation. If some agent (the player or other AI agent traveling between cities) will get in contact with the city, I will recompute the "next state" according to the actions operated by the agent. This is not relevant now anyway.

So I have these parallel entities evolving behind the scene and when a day is over (a day is defined as 5 steps of the player in the world map) I use Await.result(nextWorldState, 5 seconds) as a rendez-vous point to update the current state of the simulation. This is not necessarily how the game should run but I want to test a rendez-vous point when the rest of the game wait for the next state to be computed, because this will be probably necessary.

My problem is that after this, I dereference a lot of objects all at once and this trigger the GC collection. I tried some solutions but there's always a point when I access the result of the Future and replace the current state with it and this always trigger the GC.

Assuming that I don't want to go for regular classes (probably I will) and assuming that I don't want to split the state in multiple pieces and process them separately (I will try this but it looks unmaintanable), is there any clever solution to this problem?

Here is some not-so-pseudo code from the actual function that handle this logic:

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)

    }
  }

}

解决方案

To reduce the pain of a Full GC I suggest using G1 or CMS rather than parallel collector, and increaing the young space to reduce the number of objects promoted to tenured space however nothing beats creating less work in the first place.

The more garbage you create, the more work you do and the more work the gc has to do to clean up the objects. Creating objects faster with more CPUs won't make the GC less painful.

这篇关于使用Scala案例分类处理GC上的重负载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆