Skip to content

Commit 512795b

Browse files
committed
Implemented partial frame copies, cutting about 3/4 off the amount of data we need to copy per frame
1 parent 8bb4f80 commit 512795b

File tree

2 files changed

+61
-20
lines changed

2 files changed

+61
-20
lines changed

src/main/java/bwapi/FrameBuffer.java

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,19 @@ void enqueueFrame() {
107107
performanceMetrics.intentionallyBlocking.stopTiming();
108108
} finally { lockSize.unlock(); };
109109

110-
performanceMetrics.copyingToBuffer.time(() -> {
111-
ByteBuffer dataTarget = dataBuffer.get(indexGame());
112-
copyBuffer(liveData, dataTarget);
113-
});
110+
// For the first frame of the game, populate all buffers completely
111+
// This is to ensure all buffers have access to immutable data like regions/walkability/buildability
112+
// Afterwards, we want to shorten this process by only copying important and mutable data
113+
if (stepGame == 0) {
114+
for (ByteBuffer frameBuffer : dataBuffer) {
115+
copyBuffer(liveData, frameBuffer, true);
116+
}
117+
} else {
118+
performanceMetrics.copyingToBuffer.time(() -> {
119+
ByteBuffer dataTarget = dataBuffer.get(indexGame());
120+
copyBuffer(liveData, dataTarget, false);
121+
});
122+
}
114123

115124
lockSize.lock();
116125
try {
@@ -144,31 +153,63 @@ void dequeue() {
144153
} finally { lockSize.unlock(); }
145154
}
146155

147-
void copyBuffer(ByteBuffer source, ByteBuffer destination) {
156+
/**
157+
*
158+
* @param source Address to copy from
159+
* @param destination Address to copy to
160+
* @param size Number of bytes to copy
161+
* @return True if the copy succeeded
162+
*/
163+
private boolean tryMemcpyBuffer(ByteBuffer source, ByteBuffer destination, long offset, int size) {
164+
long addressSource = ((DirectBuffer) source).address() + offset;
165+
long addressDestination = ((DirectBuffer) destination).address() + offset;
166+
try {
167+
if (Platform.isWindows()) {
168+
if (Platform.is64Bit()) {
169+
MSVCRT.INSTANCE.memcpy(addressDestination, addressSource, size);
170+
return true;
171+
} else {
172+
MSVCRT.INSTANCE.memcpy((int) addressDestination, (int) addressSource, size);
173+
return true;
174+
}
175+
}
176+
}
177+
catch(Exception ignored) {}
178+
return false;
179+
}
180+
181+
void copyBuffer(ByteBuffer source, ByteBuffer destination, boolean copyEverything) {
148182
/*
149183
The speed at which we copy data into the frame buffer is a major cost of JBWAPI's asynchronous operation.
150-
Copy times observed in the wild range from 2.6ms - 12ms.
184+
Copy times observed in the wild usually range from 2.6ms - 19ms but are prone to large amounts of variance.
151185
152186
The normal Java way to execute this copy is via ByteBuffer.put(), which has reasonably good performance characteristics.
153187
Some experiments in 64-bit JRE have shown that using a native memcpy achieves a 35% speedup.
154188
Some experiments in 32-bit JRE show no difference in performance.
155189
156190
So, speculatively, we attempt to do a native memcpy.
157-
*/
158-
long addressSource = ((DirectBuffer) source).address();
159-
long addressDestination = ((DirectBuffer) destination).address();
160-
try {
161-
if (Platform.isWindows()) {
162-
if (Platform.is64Bit()) {
163-
MSVCRT.INSTANCE.memcpy(addressDestination, addressSource, FrameBuffer.BUFFER_SIZE);
164-
return;
165-
} else {
166-
MSVCRT.INSTANCE.memcpy((int) addressDestination, (int) addressSource, FrameBuffer.BUFFER_SIZE);
167-
return;
168-
}
191+
*/
192+
193+
if (copyEverything) {
194+
if (tryMemcpyBuffer(source, destination, 0, FrameBuffer.BUFFER_SIZE)) {
195+
return;
196+
}
197+
} else {
198+
int STATICTILES_START = 3447004; // getGroundHeight, isWalkable, isBuildable
199+
int STATICTILES_END = 4823260;
200+
int REGION_START = 5085404; // getMapTileRegionId, ..., getRegions
201+
int REGION_END = 10586480;
202+
int STRINGSSHAPES_START = 10962632; // getStringCount, ... getShapes
203+
int STRINGSHAPES_END = 32242636;
204+
int UNITFINDER_START = 32962644;
205+
if (
206+
tryMemcpyBuffer(source, destination, 0, STATICTILES_START)
207+
&& tryMemcpyBuffer(source, destination, STATICTILES_END, REGION_START - STATICTILES_END)
208+
&& tryMemcpyBuffer(source, destination, REGION_END, STRINGSSHAPES_START - REGION_END)
209+
&& tryMemcpyBuffer(source, destination, STRINGSHAPES_END, UNITFINDER_START - STRINGSHAPES_END)) {
210+
return;
169211
}
170212
}
171-
catch(Exception ignored) {}
172213

173214
// There's no specific case where we expect to fail above,
174215
// but this is a safe fallback regardless,

src/test/java/bwapi/SynchronizationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public void async_MeasurePerformance_CopyingToBuffer() {
195195
SynchronizationEnvironment environment = new SynchronizationEnvironment();
196196
environment.configuration.async = true;
197197
environment.runGame(20);
198-
final double minObserved = 2;
198+
final double minObserved = 0.25;
199199
final double maxObserved = 15;
200200
final double meanObserved = (minObserved + maxObserved) / 2;
201201
final double rangeObserved = (maxObserved - minObserved) / 2;

0 commit comments

Comments
 (0)