Skip to content

getFilterType does mod-list lookups per stack comparison — 33% of server CPU with Sophisticated Storage #37

Description

@jlmart88

Versions: CFA 2.3.0, Minecraft 1.21.1, NeoForge 21.1.224, sophisticatedcore 1.4.25, sophisticatedstorage 1.5.38. The same code is in master, so 2.5.0 is affected too.

What happened

Our server dropped to 4 TPS (~252ms per tick). A spark profile pointed at one call chain: a Sophisticated Storage hopper upgrade pushed items into a filtered controller network. Every insert attempt ran FilterLogic.stackMatchesFilter, which your MixinSophItemMatcher hooks.

Profile: https://spark.lucko.me/Sh6X6XsL8y

  • 73.5% of the server thread was in that filter chain.
  • 46.6% was inside the CFA hook (sophFilterMatcher -> CFAFilterSelector.getFilterType).
  • 33.5% of all server CPU was HashMap.getNode, called from IntegrationHandler.isModLoaded.

Cause

CFAFilterSelector.getFilterType runs once per stack-filter comparison. Each call:

  1. Clones the enum array with FilterType.values().
  2. Calls IntegrationHandler.isModLoaded for up to 4 filter types. The method memoizes, but each call still does containsKey + get on a HashMap<String, Boolean>. That is two String-hash lookups per type, per comparison.

A network with large chests makes millions of these comparisons per second, so it ends up using a lot of CPU cycles.

Fix that we tested

Added two caches in CFAFilterSelector:

  1. Compute the list of loaded filter types once. The mod list cannot change at runtime.
  2. Cache the getFilterType result per Item in a ConcurrentHashMap<Item, FilterType>. This is safe because every isSupportedFilterItem in 2.3.0 is a pure instanceof check on stack.getItem().

We patched this into the jar on our server. Heres the difference between the spark profiles:

Metric Before After
TPS 4.0 20.0
Filter chain CPU 118,892ms (73.5%) 40ms
HashMap.getNode self time 33.5% 0.6%

After profile: https://spark.lucko.me/uPWf4AHhUB

I can open a PR with this change if you want it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions