9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2026-01-06 15:51:31 +00:00
Files
Leaf/leaf-server/minecraft-patches/features/0154-Replace-brain-with-optimized-collection.patch
Dreeam 9a4efaa230 Drop patch that causes performance regression
Originally vanilla logic is to use stream, and Mojang switched it to Guava's Collections2
since 1.21.4. It is much faster than using stream or manually adding to a new ArrayList.
Manually adding to a new ArrayList requires allocating a new object array. However, the Collections2
lazy handles filter condition on iteration, so much better.
2025-08-04 19:25:56 +08:00

257 lines
14 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com>
Date: Sat, 26 Oct 2024 00:06:04 +0800
Subject: [PATCH] Replace brain with optimized collection
Co-authored-by: Taiyou06 <kaandindar21@gmail.com>
Co-authored-by: hayanesuru <hayanesuru@outlook.jp>
diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java
index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..d423f325adbdfd524d05af28f1a1097891ea1c40 100644
--- a/net/minecraft/world/entity/ai/Brain.java
+++ b/net/minecraft/world/entity/ai/Brain.java
@@ -45,14 +45,21 @@ public class Brain<E extends LivingEntity> {
static final Logger LOGGER = LogUtils.getLogger();
private final Supplier<Codec<Brain<E>>> codec;
private static final int SCHEDULE_UPDATE_DELAY = 20;
- private final Map<MemoryModuleType<?>, Optional<? extends ExpirableValue<?>>> memories = Maps.newHashMap();
- private final Map<SensorType<? extends Sensor<? super E>>, Sensor<? super E>> sensors = Maps.newLinkedHashMap();
- private final Map<Integer, Map<Activity, Set<BehaviorControl<? super E>>>> availableBehaviorsByPriority = Maps.newTreeMap();
+ // Leaf start - Replace brain maps with optimized collection
+ private final Map<MemoryModuleType<?>, Optional<? extends ExpirableValue<?>>> memories = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>();
+ private final Map<SensorType<? extends Sensor<? super E>>, Sensor<? super E>> sensors = new it.unimi.dsi.fastutil.objects.Reference2ReferenceArrayMap<>();
+ private final Map<Integer, Map<Activity, Set<BehaviorControl<? super E>>>> availableBehaviorsByPriority = new it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap<>();
+ public static final Map[] EMPTY_MAP_ARRAY = {};
+ public static final Set[] EMPTY_SET_ARRAY = {};
+ public static final org.dreeam.leaf.util.map.BehaviorControlArraySet[] EMPTY_BEHAVIOR_ARRAY = {};
+ private Map<Activity, Set<BehaviorControl<? super E>>>[] availableBehaviorsByPriorityArray = EMPTY_MAP_ARRAY;
+ private org.dreeam.leaf.util.map.BehaviorControlArraySet<? super E>[] activeBehaviors = EMPTY_BEHAVIOR_ARRAY;
private Schedule schedule = Schedule.EMPTY;
- private final Map<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> activityRequirements = Maps.newHashMap();
- private final Map<Activity, Set<MemoryModuleType<?>>> activityMemoriesToEraseWhenStopped = Maps.newHashMap();
- private Set<Activity> coreActivities = Sets.newHashSet();
- private final Set<Activity> activeActivities = Sets.newHashSet();
+ private final Map<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> activityRequirements = new org.dreeam.leaf.util.map.ActivityArrayMap<>(EMPTY_SET_ARRAY);
+ private final Map<Activity, Set<MemoryModuleType<?>>> activityMemoriesToEraseWhenStopped = new org.dreeam.leaf.util.map.ActivityArrayMap<>(EMPTY_SET_ARRAY);
+ private Set<Activity> coreActivities = Set.of();
+ private final Set<Activity> activeActivities = new org.dreeam.leaf.util.map.ActivityBitSet();
+ // Leaf end - Replace brain maps with optimized collection
private Activity defaultActivity = Activity.IDLE;
private long lastScheduleUpdate = -9999L;
@@ -162,6 +169,8 @@ public class Brain<E extends LivingEntity> {
for (Brain.MemoryValue<?> memoryValue : memoryValues) {
memoryValue.setMemoryInternal(this);
}
+
+ ((it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<MemoryModuleType<?>, Optional<? extends ExpirableValue<?>>>) this.memories).trim(); // Leaf
}
public <T> DataResult<T> serializeStart(DynamicOps<T> ops) {
@@ -178,6 +187,8 @@ public class Brain<E extends LivingEntity> {
public void clearMemories() {
this.memories.keySet().forEach(memoryModuleType -> this.memories.put((MemoryModuleType<?>)memoryModuleType, Optional.empty()));
+ this.availableBehaviorsByPriorityArray = EMPTY_MAP_ARRAY; // Leaf - Replace brain maps with optimized collection
+ this.activeBehaviors = EMPTY_BEHAVIOR_ARRAY; // Leaf - Replace brain maps with optimized collection
}
public <U> void eraseMemory(MemoryModuleType<U> type) {
@@ -362,7 +373,7 @@ public class Brain<E extends LivingEntity> {
}
public void addActivity(Activity activity, ImmutableList<? extends Pair<Integer, ? extends BehaviorControl<? super E>>> tasks) {
- this.addActivityAndRemoveMemoriesWhenStopped(activity, tasks, ImmutableSet.of(), Sets.newHashSet());
+ this.addActivityAndRemoveMemoriesWhenStopped(activity, tasks, ImmutableSet.of(), new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>());
}
public void addActivityWithConditions(
@@ -395,15 +406,18 @@ public class Brain<E extends LivingEntity> {
for (Pair<Integer, ? extends BehaviorControl<? super E>> pair : tasks) {
this.availableBehaviorsByPriority
- .computeIfAbsent(pair.getFirst(), integer -> Maps.newHashMap())
- .computeIfAbsent(activity, activity1 -> Sets.newLinkedHashSet())
+ .computeIfAbsent(pair.getFirst(), integer -> new org.dreeam.leaf.util.map.ActivityArrayMap<>((Set<BehaviorControl<? super E>>[]) EMPTY_SET_ARRAY)) // Leaf - Replace brain maps with optimized collection
+ .computeIfAbsent(activity, activity1 -> new org.dreeam.leaf.util.map.BehaviorControlArraySet<>()) // Leaf - Replace brain maps with optimized collection
.add((BehaviorControl<? super E>)pair.getSecond());
}
+ if (this.activeBehaviors != EMPTY_BEHAVIOR_ARRAY) this.activeBehaviors = EMPTY_BEHAVIOR_ARRAY; // Leaf - Replace brain maps with optimized collection
}
@VisibleForTesting
public void removeAllBehaviors() {
this.availableBehaviorsByPriority.clear();
+ this.availableBehaviorsByPriorityArray = EMPTY_MAP_ARRAY; // Leaf - Replace brain maps with optimized collection
+ this.activeBehaviors = EMPTY_BEHAVIOR_ARRAY; // Leaf - Replace brain maps with optimized collection
}
public boolean isActive(Activity activity) {
@@ -437,14 +451,28 @@ public class Brain<E extends LivingEntity> {
}
private void forgetOutdatedMemories() {
+ boolean flag = false;
+ for (var entry : this.memories.values()) {
+ if (entry.isPresent()) {
+ ExpirableValue<?> expirableValue = entry.get();
+ if (expirableValue.hasExpired()) {
+ flag = true;
+ }
+ expirableValue.tick();
+ }
+ }
+ if (flag) {
+ eraseOutdatedMemories();
+ }
+ }
+
+ private void eraseOutdatedMemories() {
for (Entry<MemoryModuleType<?>, Optional<? extends ExpirableValue<?>>> entry : this.memories.entrySet()) {
if (entry.getValue().isPresent()) {
- ExpirableValue<?> expirableValue = (ExpirableValue<?>)entry.getValue().get();
+ ExpirableValue<?> expirableValue = entry.getValue().get();
if (expirableValue.hasExpired()) {
this.eraseMemory(entry.getKey());
}
-
- expirableValue.tick();
}
}
}
@@ -452,34 +480,95 @@ public class Brain<E extends LivingEntity> {
public void stopAll(ServerLevel level, E owner) {
long gameTime = owner.level().getGameTime();
- for (BehaviorControl<? super E> behaviorControl : this.getRunningBehaviors()) {
- behaviorControl.doStop(level, owner, gameTime);
+ // Leaf start - Replace brain maps with optimized collection
+ if (this.availableBehaviorsByPriorityArray.length != this.availableBehaviorsByPriority.size()) {
+ this.availableBehaviorsByPriorityArray = this.availableBehaviorsByPriority.values().toArray(EMPTY_MAP_ARRAY);
+ }
+ for (Map<Activity, Set<BehaviorControl<? super E>>> map : availableBehaviorsByPriorityArray) {
+ var map1 = (org.dreeam.leaf.util.map.ActivityArrayMap<Set<BehaviorControl<? super E>>>) map;
+ for (int index = 0, size = map1.size(); index < size; index++) {
+ var behaviorControls = (org.dreeam.leaf.util.map.BehaviorControlArraySet<? super E>) map1.v[index];
+ var behaviorControlsRaw = behaviorControls.raw();
+ for (int i = 0, size1 = behaviorControls.size(); i < size1; i++) {
+ BehaviorControl<? super E> behaviorControl = behaviorControlsRaw[i];
+ if (behaviorControl.getStatus() == Behavior.Status.RUNNING) {
+ behaviorControl.doStop(level, owner, gameTime);
+ behaviorControls.dec();
+ }
+ }
+ }
}
+ // Leaf end - Replace brain maps with optimized collection
}
private void startEachNonRunningBehavior(ServerLevel level, E entity) {
long gameTime = level.getGameTime();
- for (Map<Activity, Set<BehaviorControl<? super E>>> map : this.availableBehaviorsByPriority.values()) {
- for (Entry<Activity, Set<BehaviorControl<? super E>>> entry : map.entrySet()) {
- Activity activity = entry.getKey();
- if (this.activeActivities.contains(activity)) {
- for (BehaviorControl<? super E> behaviorControl : entry.getValue()) {
- if (behaviorControl.getStatus() == Behavior.Status.STOPPED) {
- behaviorControl.tryStart(level, entity, gameTime);
- }
+ // Leaf start - Replace brain maps with optimized collection
+ if (this.availableBehaviorsByPriorityArray.length != this.availableBehaviorsByPriority.size()) {
+ this.availableBehaviorsByPriorityArray = this.availableBehaviorsByPriority.values().toArray(EMPTY_MAP_ARRAY);
+ }
+ var aact = (org.dreeam.leaf.util.map.ActivityBitSet) this.activeActivities;
+ if (activeBehaviors == EMPTY_BEHAVIOR_ARRAY || aact.unsetDirty()) {
+ var list = it.unimi.dsi.fastutil.objects.ReferenceArrayList.wrap(activeBehaviors);
+ list.clear();
+ int activeSet = aact.bitSet();
+ for (Map<Activity, Set<BehaviorControl<? super E>>> map : availableBehaviorsByPriorityArray) {
+ for (int index = 0; index < org.dreeam.leaf.util.RegistryTypeManager.ACTIVITY_SIZE; index++) {
+ if ((activeSet & (1 << index)) == 0) {
+ continue;
+ }
+ var ele = ((org.dreeam.leaf.util.map.ActivityArrayMap<Set<BehaviorControl<? super E>>>) map).getValue(index);
+ if (ele == null) {
+ continue;
+ }
+ list.add((org.dreeam.leaf.util.map.BehaviorControlArraySet<? super E>) ele);
+ }
+ }
+ activeBehaviors = it.unimi.dsi.fastutil.objects.ObjectArrays.setLength(list.elements(), list.size());
+ }
+ for (int index = 0, size = activeBehaviors.length; index < size; index++) {
+ var behaviorControls = activeBehaviors[index];
+ var behaviorControlsRaw = behaviorControls.raw();
+ for (int i = 0, size1 = behaviorControls.size(); i < size1; i++) {
+ BehaviorControl<? super E> behaviorControl = behaviorControlsRaw[i];
+ if (behaviorControl.getStatus() == Behavior.Status.STOPPED) {
+ if (behaviorControl.tryStart(level, entity, gameTime)) {
+ behaviorControls.inc();
}
}
}
}
+ // Leaf end - Replace brain maps with optimized collection
}
private void tickEachRunningBehavior(ServerLevel level, E entity) {
long gameTime = level.getGameTime();
- for (BehaviorControl<? super E> behaviorControl : this.getRunningBehaviors()) {
- behaviorControl.tickOrStop(level, entity, gameTime);
+ // Leaf start - Replace brain maps with optimized collection
+ if (this.availableBehaviorsByPriorityArray.length != this.availableBehaviorsByPriority.size()) {
+ this.availableBehaviorsByPriorityArray = this.availableBehaviorsByPriority.values().toArray(new Map[0]);
+ }
+ for (Map<Activity, Set<BehaviorControl<? super E>>> map : availableBehaviorsByPriorityArray) {
+ var map1 = (org.dreeam.leaf.util.map.ActivityArrayMap<Set<BehaviorControl<? super E>>>) map;
+ for (int index = 0, size = map1.size(); index < size; index++) {
+ var behaviorControls = (org.dreeam.leaf.util.map.BehaviorControlArraySet<? super E>) map1.v[index];
+ if (!behaviorControls.running()) {
+ continue;
+ }
+ BehaviorControl<? super E>[] behaviorControlsRaw = behaviorControls.raw();
+ for (int i = 0, size1 = behaviorControls.size(); i < size1; i++) {
+ BehaviorControl<? super E> behaviorControl = behaviorControlsRaw[i];
+ if (behaviorControl.getStatus() == Behavior.Status.RUNNING) {
+ behaviorControl.tickOrStop(level, entity, gameTime);
+ if (behaviorControl.getStatus() == Behavior.Status.STOPPED) {
+ behaviorControls.dec();
+ }
+ }
+ }
+ }
}
+ // Leaf end - Replace brain maps with optimized collection
}
private boolean activityRequirementsAreMet(Activity activity) {
diff --git a/net/minecraft/world/entity/schedule/Activity.java b/net/minecraft/world/entity/schedule/Activity.java
index 5a143bb6fabba3dc4e2272afb0be636d5722ea22..7753b2b2ab00c08dc15444a1caa08faac1d82f0c 100644
--- a/net/minecraft/world/entity/schedule/Activity.java
+++ b/net/minecraft/world/entity/schedule/Activity.java
@@ -32,10 +32,12 @@ public class Activity {
public static final Activity DIG = register("dig");
private final String name;
private final int hashCode;
+ public final int id; // Leaf
- private Activity(String name) {
+ private Activity(String name, int id) { // Leaf
this.name = name;
this.hashCode = name.hashCode();
+ this.id = id; // Leaf
}
public String getName() {
@@ -43,7 +45,7 @@ public class Activity {
}
private static Activity register(String key) {
- return Registry.register(BuiltInRegistries.ACTIVITY, key, new Activity(key));
+ return Registry.register(BuiltInRegistries.ACTIVITY, key, new Activity(key, BuiltInRegistries.ACTIVITY.size())); // Leaf
}
@Override