Skip to content

Commit f91237d

Browse files
committed
improve performance of set(component) and entity
1 parent f24600f commit f91237d

3 files changed

Lines changed: 125 additions & 71 deletions

File tree

examples/src/main/java/com/github/elebras1/flecs/examples/BulkExample.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ public static void main(String[] args) {
2020

2121
long startCreate = System.nanoTime();
2222

23-
EcsLongList entityIds = world.entityBulk(totalEntities);
24-
for (long entityId : entityIds) {
23+
for (int i = 0; i < totalEntities; i++) {
24+
long entityId = world.entity();
2525
Entity entity = world.obtainEntity(entityId);
2626
entity.set(new Position(entityId % 100, entityId % 100));
2727
entity.set(new Velocity(1, 1));

src/main/java/com/github/elebras1/flecs/Entity.java

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import java.lang.foreign.ValueLayout;
66

77
public class Entity {
8-
8+
99
private final Flecs world;
1010
private final long id;
1111

@@ -112,13 +112,11 @@ public <T> Entity set(T data) {
112112
Class<T> componentClass = (Class<T>) data.getClass();
113113
long componentId = this.world.componentRegistry().getComponentId(componentClass);
114114
Component<T> component = this.world.componentRegistry().getComponent(componentClass);
115-
try (Arena tempArena = Arena.ofConfined()) {
116-
MemorySegment dataSegment = tempArena.allocate(component.layout());
117115

118-
component.write(dataSegment, data);
116+
MemorySegment dataSegment = this.world.getComponentBuffer(component.size());
117+
component.write(dataSegment, data);
118+
flecs_h.ecs_set_id(this.world.nativeHandle(), this.id, componentId, component.size(), dataSegment);
119119

120-
flecs_h.ecs_set_id(this.world.nativeHandle(), this.id, componentId, component.size(), dataSegment);
121-
}
122120
return this;
123121
}
124122

@@ -150,30 +148,30 @@ public void disable() {
150148

151149
public FlecsObserver observe(long eventId, Runnable callback) {
152150
return this.world.observer()
153-
.event(eventId)
154-
.with(FlecsConstants.EcsAny)
155-
.each((entityId) -> {
156-
if (entityId == this.id) {
157-
callback.run();
158-
}
159-
});
151+
.event(eventId)
152+
.with(FlecsConstants.EcsAny)
153+
.each((entityId) -> {
154+
if (entityId == this.id) {
155+
callback.run();
156+
}
157+
});
160158
}
161159

162160
public <T> FlecsObserver observe(Class<T> eventClass, java.util.function.Consumer<T> callback) {
163161
long eventId = this.world.componentRegistry().getComponentId(eventClass);
164162
return this.world.observer()
165-
.event(eventId)
166-
.with(FlecsConstants.EcsAny)
167-
.iter((it) -> {
168-
for (int i = 0; i < it.count(); i++) {
169-
if (it.entityId(i) == this.id) {
170-
T eventData = this.get(eventClass);
171-
if (eventData != null) {
172-
callback.accept(eventData);
163+
.event(eventId)
164+
.with(FlecsConstants.EcsAny)
165+
.iter((it) -> {
166+
for (int i = 0; i < it.count(); i++) {
167+
if (it.entityId(i) == this.id) {
168+
T eventData = this.get(eventClass);
169+
if (eventData != null) {
170+
callback.accept(eventData);
171+
}
173172
}
174173
}
175-
}
176-
});
174+
});
177175
}
178176

179177
public void emit(long eventId) {
@@ -223,12 +221,12 @@ public boolean equals(Object obj) {
223221
}
224222
return this.id == other.id && this.world == other.world;
225223
}
226-
224+
227225
@Override
228226
public int hashCode() {
229227
return Long.hashCode(this.id);
230228
}
231-
229+
232230
@Override
233231
public String toString() {
234232
String name = this.getName();
@@ -237,5 +235,4 @@ public String toString() {
237235
}
238236
return String.format("Entity[%d]", this.id);
239237
}
240-
}
241-
238+
}

src/main/java/com/github/elebras1/flecs/Flecs.java

Lines changed: 99 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,72 +16,119 @@
1616
import static java.lang.foreign.ValueLayout.JAVA_LONG;
1717

1818
public class Flecs implements AutoCloseable {
19-
19+
2020
private final MemorySegment nativeWorld;
2121
private final Arena arena;
2222
private final ComponentRegistry componentRegistry;
2323
private boolean closed = false;
2424
private final ThreadLocal<NameBuffer> threadLocalNameBuffer;
25+
private final ThreadLocal<ComponentBuffer> threadLocalComponentBuffer;
26+
private final ThreadLocal<EntityDescBuffer> threadLocalEntityDesc;
2527
private final Map<Long, SystemCallbacks> systemCallbacks;
2628
private final Map<Long, ObserverCallbacks> observerCallbacks;
2729

2830
static {
2931
FlecsLoader.load();
3032
}
3133

32-
private static final class SystemCallbacks {
33-
final Query.IterCallback iterCallback;
34-
final Query.RunCallback runCallback;
35-
final Query.EntityCallback entityCallback;
36-
37-
SystemCallbacks(Query.IterCallback iterCallback, Query.RunCallback runCallback, Query.EntityCallback entityCallback) {
38-
this.iterCallback = iterCallback;
39-
this.runCallback = runCallback;
40-
this.entityCallback = entityCallback;
41-
}
34+
private record SystemCallbacks(Query.IterCallback iterCallback, Query.RunCallback runCallback, Query.EntityCallback entityCallback) {
4235
}
4336

44-
private static final class ObserverCallbacks {
45-
final Query.IterCallback iterCallback;
46-
final Query.RunCallback runCallback;
47-
final Query.EntityCallback entityCallback;
48-
49-
ObserverCallbacks(Query.IterCallback iterCallback, Query.RunCallback runCallback, Query.EntityCallback entityCallback) {
50-
this.iterCallback = iterCallback;
51-
this.runCallback = runCallback;
52-
this.entityCallback = entityCallback;
53-
}
37+
private record ObserverCallbacks(Query.IterCallback iterCallback, Query.RunCallback runCallback, Query.EntityCallback entityCallback) {
5438
}
5539

56-
private static final class NameBuffer {
57-
MemorySegment segment;
58-
int capacity;
40+
private static final class NameBuffer implements AutoCloseable {
41+
private Arena arena;
42+
private MemorySegment segment;
43+
private int capacity;
5944

6045
NameBuffer(int initialCapacity) {
6146
this.capacity = initialCapacity;
62-
this.segment = Arena.ofAuto().allocate(initialCapacity);
47+
this.arena = Arena.ofConfined();
48+
this.segment = this.arena.allocate(initialCapacity);
6349
}
6450

6551
MemorySegment ensure(int needed) {
6652
if (needed > capacity) {
67-
Arena arena = Arena.ofAuto();
68-
segment = arena.allocate(needed);
69-
capacity = needed;
53+
this.arena.close();
54+
this.arena = Arena.ofConfined();
55+
this.segment = this.arena.allocate(needed);
56+
this.capacity = needed;
7057
}
7158
return segment;
7259
}
60+
61+
@Override
62+
public void close() {
63+
if (this.arena != null) {
64+
this.arena.close();
65+
this.arena = null;
66+
}
67+
}
68+
}
69+
70+
private static final class ComponentBuffer implements AutoCloseable {
71+
private Arena arena;
72+
private MemorySegment segment;
73+
private long capacity;
74+
75+
ComponentBuffer(long initialCapacity) {
76+
this.capacity = initialCapacity;
77+
this.arena = Arena.ofConfined();
78+
this.segment = this.arena.allocate(initialCapacity);
79+
}
80+
81+
MemorySegment ensure(long needed) {
82+
if (needed > capacity) {
83+
long newCapacity = Math.max(needed, capacity * 2);
84+
this.arena.close();
85+
this.arena = Arena.ofConfined();
86+
this.segment = this.arena.allocate(newCapacity);
87+
this.capacity = newCapacity;
88+
}
89+
return segment.asSlice(0, needed);
90+
}
91+
92+
@Override
93+
public void close() {
94+
if (this.arena != null) {
95+
this.arena.close();
96+
this.arena = null;
97+
}
98+
}
99+
}
100+
101+
private static final class EntityDescBuffer implements AutoCloseable {
102+
private final Arena arena;
103+
private final MemorySegment segment;
104+
105+
EntityDescBuffer() {
106+
this.arena = Arena.ofConfined();
107+
this.segment = ecs_entity_desc_t.allocate(this.arena);
108+
}
109+
110+
MemorySegment get() {
111+
return this.segment;
112+
}
113+
114+
@Override
115+
public void close() {
116+
this.arena.close();
117+
}
73118
}
74119

75120
public Flecs() {
76121
this.arena = Arena.ofConfined();
77122
this.nativeWorld = flecs_h.ecs_init();
78-
123+
79124
if (this.nativeWorld == null || this.nativeWorld.address() == 0) {
80125
throw new IllegalStateException("Flecs world initialization failed");
81126
}
82127

83128
this.componentRegistry = new ComponentRegistry(this);
84129
this.threadLocalNameBuffer = ThreadLocal.withInitial(() -> new NameBuffer(64));
130+
this.threadLocalComponentBuffer = ThreadLocal.withInitial(() -> new ComponentBuffer(256));
131+
this.threadLocalEntityDesc = ThreadLocal.withInitial(EntityDescBuffer::new);
85132
this.systemCallbacks = new ConcurrentHashMap<>();
86133
this.observerCallbacks = new ConcurrentHashMap<>();
87134
}
@@ -103,11 +150,10 @@ public long entity(String name) {
103150
nameSegment.asSlice(0, len).copyFrom(MemorySegment.ofArray(utf8));
104151
nameSegment.set(ValueLayout.JAVA_BYTE, len, (byte)0);
105152

106-
try (Arena tempArena = Arena.ofConfined()) {
107-
MemorySegment desc = ecs_entity_desc_t.allocate(tempArena);
108-
ecs_entity_desc_t.name(desc, nameSegment);
109-
return flecs_h.ecs_entity_init(this.nativeWorld, desc);
110-
}
153+
MemorySegment desc = this.threadLocalEntityDesc.get().get();
154+
desc.fill((byte) 0);
155+
ecs_entity_desc_t.name(desc, nameSegment);
156+
return flecs_h.ecs_entity_init(this.nativeWorld, desc);
111157
}
112158

113159
public Entity obtainEntity(long entityId) {
@@ -117,18 +163,23 @@ public Entity obtainEntity(long entityId) {
117163
return new Entity(this, entityId);
118164
}
119165

166+
MemorySegment getComponentBuffer(long size) {
167+
return this.threadLocalComponentBuffer.get().ensure(size);
168+
}
169+
120170
public EcsLongList entityBulk(int count) {
121171
this.checkClosed();
172+
try(Arena tempArena = Arena.ofConfined()) {
173+
MemorySegment desc = ecs_bulk_desc_t.allocate(tempArena);
174+
ecs_bulk_desc_t.count(desc, count);
175+
ecs_bulk_desc_t.entities(desc, MemorySegment.NULL);
122176

123-
MemorySegment desc = ecs_bulk_desc_t.allocate(this.arena);
124-
ecs_bulk_desc_t.count(desc, count);
125-
ecs_bulk_desc_t.entities(desc, MemorySegment.NULL);
177+
MemorySegment idsSegment = flecs_h.ecs_bulk_init(this.nativeWorld, desc);
126178

127-
MemorySegment idsSegment = flecs_h.ecs_bulk_init(nativeWorld, desc);
128-
129-
EcsLongList ids = new EcsLongList(count);
130-
ids.addAll(idsSegment.asSlice(0, (long) count * Long.BYTES).toArray(JAVA_LONG));
131-
return ids;
179+
EcsLongList ids = new EcsLongList(count);
180+
ids.addAll(idsSegment.asSlice(0, (long) count * Long.BYTES).toArray(JAVA_LONG));
181+
return ids;
182+
}
132183
}
133184

134185
public long makeAlive(long entityId) {
@@ -357,6 +408,12 @@ public void close() {
357408
}
358409
this.arena.close();
359410
}
411+
this.threadLocalNameBuffer.get().close();
412+
this.threadLocalComponentBuffer.get().close();
413+
this.threadLocalEntityDesc.get().close();
414+
this.threadLocalNameBuffer.remove();
415+
this.threadLocalComponentBuffer.remove();
416+
this.threadLocalEntityDesc.remove();
360417
}
361418

362419
@Override

0 commit comments

Comments
 (0)