Skip to content

Commit

Permalink
use ConcurrentHashMap in EvalCacheMulti
Browse files Browse the repository at this point in the history
hopefully solves a case of CPU hogging in AnyRefMap.get

```
java.lang.Thread.State: RUNNABLE
at scala.collection.mutable.AnyRefMap.seekEntry(AnyRefMap.scala:126)
at scala.collection.mutable.AnyRefMap.get(AnyRefMap.scala:151)
at lila.ws.evalCache.EvalCacheMulti.unregisterEval(EvalCacheMulti.scala:65)
at lila.ws.evalCache.EvalCacheMulti.register$$anonfun$1$$anonfun$1(EvalCacheMulti.scala:33)
at lila.ws.evalCache.EvalCacheMulti$$Lambda/0x0000796c44839ba0.applyVoid(Unknown Source)
at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
at scala.collection.immutable.List.foreach(List.scala:333)
at lila.ws.evalCache.EvalCacheMulti.register$$anonfun$1(EvalCacheMulti.scala:33)
at lila.ws.evalCache.EvalCacheMulti$$Lambda/0x0000796c447ffa08.applyVoid(Unknown Source)
at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
at scala.Option.foreach(Option.scala:437)
at lila.ws.evalCache.EvalCacheMulti.register(EvalCacheMulti.scala:33)
at lila.ws.evalCache.EvalCacheApi.getMulti(EvalCacheApi.scala:54)
at lila.ws.ClientActor$.globalReceive(ClientActor.scala:115)
```

and

```
java.lang.Thread.State: RUNNABLE
at scala.collection.mutable.AnyRefMap.seekEntry(AnyRefMap.scala:126)
at scala.collection.mutable.AnyRefMap.get(AnyRefMap.scala:151)
at lila.ws.evalCache.EvalCacheMulti.register$$anonfun$2(EvalCacheMulti.scala:37)
at lila.ws.evalCache.EvalCacheMulti$$Lambda/0x0000796c44804000.apply(Unknown Source)
at scala.collection.immutable.List.foreach(List.scala:333)
at lila.ws.evalCache.EvalCacheMulti.register(EvalCacheMulti.scala:37)
at lila.ws.evalCache.EvalCacheApi.getMulti(EvalCacheApi.scala:54)
at lila.ws.ClientActor$.globalReceive(ClientActor.scala:115)
```
  • Loading branch information
ornicar committed Mar 13, 2024
1 parent b98c61b commit d13fa47
Showing 1 changed file with 25 additions and 26 deletions.
51 changes: 25 additions & 26 deletions src/main/scala/evalCache/EvalCacheMulti.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package evalCache
import chess.format.Fen
import chess.variant.Variant

import scala.collection.mutable
import java.util.concurrent.ConcurrentHashMap

import lila.ws.ipc.ClientIn.EvalHitMulti
import lila.ws.ipc.ClientOut.EvalGetMulti
Expand All @@ -20,32 +20,33 @@ final private class EvalCacheMulti(using
import EvalCacheMulti.*
import EvalCacheUpgrade.{ EvalState, SetupId, SriString }

private val members = mutable.AnyRefMap.empty[SriString, WatchingMember]
private val evals = mutable.AnyRefMap.empty[SetupId, EvalState]
private val members = ConcurrentHashMap[SriString, WatchingMember](4096)
private val evals = ConcurrentHashMap[SetupId, EvalState](1024)
private val expirableSris = ExpireCallbackMemo[Sri](scheduler, 1 minute, expire)

private val upgradeMon = Monitor.evalCache.multi.upgrade

def register(sri: Sri, e: EvalGetMulti): Unit =
members
.get(sri.value)
.foreach: prevMember =>
prevMember.setups.foreach(unregisterEval(_, sri))
Option(members.get(sri.value)).foreach:
_.setups.foreach(unregisterEval(_, sri))
val wm = WatchingMember(sri, e.variant, e.fens)
members += (sri.value -> wm)
members.put(sri.value, wm)
wm.setups.foreach: setupId =>
evals += (setupId -> evals.get(setupId).fold(EvalState(Set(sri), Depth(0)))(_.addSri(sri)))
evals.compute(setupId, (_, prev) => Option(prev).fold(EvalState(Set(sri), Depth(0)))(_.addSri(sri)))
expirableSris.put(sri)

def onEval(input: EvalCacheEntry.Input, fromSri: Sri): Unit =
val setupId = makeSetupId(input.id.variant, input.fen)
evals
.get(setupId)
.filter(_.depth < input.eval.depth)
.map(setupId -> _)
.foreach: (setupId, oldEval) =>
evals += (setupId -> oldEval.copy(depth = input.eval.depth))
val sris = oldEval.sris.filter(_ != fromSri)
Option(
evals.computeIfPresent(
setupId,
(_, ev) =>
if ev.depth >= input.eval.depth then ev
else ev.copy(depth = input.eval.depth)
)
).filter(_.depth == input.eval.depth)
.foreach: eval =>
val sris = eval.sris.filter(_ != fromSri)
if sris.nonEmpty then
val hit = EvalHitMulti:
EvalCacheJsonHandlers.writeMultiHit(input.fen, input.eval)
Expand All @@ -54,19 +55,17 @@ final private class EvalCacheMulti(using
upgradeMon.count.increment(sris.size)

private def expire(sri: Sri): Unit =
members
.get(sri.value)
.foreach: wm =>
wm.setups.foreach(unregisterEval(_, sri))
members -= sri.value
Option(members.remove(sri.value)).foreach: wm =>
wm.setups.foreach(unregisterEval(_, sri))

private def unregisterEval(setupId: SetupId, sri: Sri): Unit =
evals
.get(setupId)
.foreach: eval =>
evals.computeIfPresent(
setupId,
(_, eval) =>
val newSris = eval.sris - sri
if newSris.isEmpty then evals -= setupId
else evals += (setupId -> eval.copy(sris = newSris))
if newSris.isEmpty then null
else eval.copy(sris = newSris)
)

scheduler.scheduleWithFixedDelay(1 minute, 1 minute): () =>
upgradeMon.members.update(members.size)
Expand Down

0 comments on commit d13fa47

Please sign in to comment.