Skip to content

Commit 3159f73

Browse files
committed
Fixed erroneous conversion between nanoseconds and milliseconds. First pass at performance metrics.
1 parent 4cf39fc commit 3159f73

File tree

10 files changed

+255
-48
lines changed

10 files changed

+255
-48
lines changed

src/main/java/bwapi/BWClient.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
public class BWClient {
99
private final BWEventListener eventListener;
1010
private BotWrapper botWrapper;
11+
private PerformanceMetrics performanceMetrics;
1112

1213
public BWClient(final BWEventListener eventListener) {
1314
Objects.requireNonNull(eventListener);
@@ -22,6 +23,14 @@ public Game getGame() {
2223
return botWrapper == null ? null : botWrapper.getGame();
2324
}
2425

26+
/**
27+
* Returns JBWAPI performance metrics.
28+
* Metrics will be mostly empty if metrics collection isn't timersEnabled in the bot configuration
29+
*/
30+
public PerformanceMetrics performanceMetrics() {
31+
return performanceMetrics;
32+
}
33+
2534
public void startGame() {
2635
BWClientConfiguration configuration = new BWClientConfiguration();
2736
startGame(configuration);
@@ -59,16 +68,21 @@ public void startGame(BWClientConfiguration configuration) {
5968
}
6069
client.update();
6170
if (liveGameData.isInGame()) {
62-
botWrapper.startNewGame(client.mapFile());
71+
performanceMetrics = new PerformanceMetrics(configuration);
72+
botWrapper.startNewGame(client.mapFile(), performanceMetrics);
6373
}
6474
}
6575
while (liveGameData.isInGame()) {
66-
botWrapper.onFrame();
67-
getGame().sideEffects.flushTo(liveGameData);
68-
client.update();
69-
if (!client.isConnected()) {
70-
client.reconnect();
71-
}
76+
performanceMetrics.totalFrameDuration.time(() -> {
77+
botWrapper.onFrame();
78+
performanceMetrics.flushSideEffects.time(() -> {
79+
getGame().sideEffects.flushTo(liveGameData);
80+
});
81+
client.update();
82+
if (!client.isConnected()) {
83+
client.reconnect();
84+
}
85+
});
7286
}
7387
botWrapper.endGame();
7488
} while (configuration.autoContinue);

src/main/java/bwapi/BWClientConfiguration.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class BWClientConfiguration {
3232
* If JBWAPI detects that this much time (in nanoseconds) has passed since a bot's event handlers began, returns control back to BWAPI.
3333
* Real-time human play typically uses the "fastest" game speed, which has 42.86ms (42,860ns) between frames.
3434
*/
35-
public int asyncFrameDurationNanos = 40000;
35+
public int asyncFrameDurationMs = 40000;
3636

3737
/**
3838
* The maximum number of frames to buffer while waiting on a bot.
@@ -51,16 +51,16 @@ public class BWClientConfiguration {
5151

5252
/**
5353
* Enables collection of diagnostics.
54-
* When enabled, JBWAPI collects and publishes performance metrics.
54+
* When timersEnabled, JBWAPI collects and publishes performance metrics.
5555
*/
5656
public boolean diagnosePerformance = false;
5757

5858
/**
5959
* Checks that the configuration is in a valid state. Throws an IllegalArgumentException if it isn't.
6060
*/
6161
public void validate() {
62-
if (async && asyncFrameDurationNanos < 0) {
63-
throw new IllegalArgumentException("asyncFrameDurationNanos needs to be a non-negative number (it's how long JBWAPI waits for a bot response before returning control to BWAPI).");
62+
if (async && asyncFrameDurationMs < 0) {
63+
throw new IllegalArgumentException("asyncFrameDurationMs needs to be a non-negative number (it's how long JBWAPI waits for a bot response before returning control to BWAPI).");
6464
}
6565
if (async && asyncFrameBufferSize < 1) {
6666
throw new IllegalArgumentException("asyncFrameBufferSize needs to be a positive number (There needs to be at least one frame buffer).");

src/main/java/bwapi/BotWrapper.java

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class BotWrapper {
3838
private Game game;
3939
private Thread botThread;
4040
private boolean gameOver;
41+
private PerformanceMetrics performanceMetrics;
4142

4243
BotWrapper(BWClientConfiguration configuration, BWEventListener eventListener) {
4344
this.configuration = configuration;
@@ -48,8 +49,9 @@ class BotWrapper {
4849
/**
4950
* Resets the BotWrapper for a new game.
5051
*/
51-
void startNewGame(ByteBuffer dataSource) {
52-
frameBuffer.initialize(dataSource);
52+
void startNewGame(ByteBuffer dataSource, PerformanceMetrics performanceMetrics) {
53+
frameBuffer.initialize(dataSource, performanceMetrics);
54+
this.performanceMetrics = performanceMetrics;
5355
game = new Game(liveClientData);
5456
liveClientData.setBuffer(dataSource);
5557
botThread = null;
@@ -70,16 +72,17 @@ Game getGame() {
7072
void onFrame() {
7173
if (configuration.async) {
7274
long startNanos = System.nanoTime();
73-
long endNanos = startNanos + configuration.asyncFrameDurationNanos;
75+
long endNanos = startNanos + configuration.asyncFrameDurationMs * 1000000;
7476
if (botThread == null) {
7577
botThread = createBotThread();
7678
botThread.setName("JBWAPI Bot");
7779
botThread.start();
7880
}
79-
frameBuffer.enqueueFrame();
81+
performanceMetrics.copyingToBuffer.time(frameBuffer::enqueueFrame);
8082
frameBuffer.lockSize.lock();
8183
try {
8284
while (frameBuffer.empty()) {
85+
performanceMetrics.bwapiResponse.startTiming();
8386
if (configuration.asyncWaitOnFrameZero && liveClientData.gameData().getFrameCount() == 0) {
8487
frameBuffer.conditionSize.await();
8588
} else {
@@ -88,6 +91,7 @@ void onFrame() {
8891
frameBuffer.conditionSize.awaitNanos(remainingNanos);
8992
}
9093
}
94+
performanceMetrics.bwapiResponse.stopTiming();
9195
} catch(InterruptedException ignored) {
9296
} finally {
9397
frameBuffer.lockSize.unlock();
@@ -112,23 +116,33 @@ private Thread createBotThread() {
112116
return new Thread(() -> {
113117
while ( ! gameOver) {
114118
frameBuffer.lockSize.lock();
115-
try { while (frameBuffer.empty()) frameBuffer.conditionSize.awaitUninterruptibly(); } finally { frameBuffer.lockSize.unlock(); }
116-
119+
try {
120+
while (frameBuffer.empty()) {
121+
performanceMetrics.botIdle.startTiming();
122+
frameBuffer.conditionSize.awaitUninterruptibly();
123+
}
124+
performanceMetrics.botIdle.stopTiming();
125+
} finally {
126+
frameBuffer.lockSize.unlock();
127+
}
117128
game.clientData().setBuffer(frameBuffer.peek());
129+
performanceMetrics.frameBufferSize.record(frameBuffer.framesBuffered());
118130
handleEvents();
119131
frameBuffer.dequeue();
120132
}
121133
});
122134
}
123135

124136
private void handleEvents() {
125-
ClientData.GameData gameData = game.clientData().gameData();
126-
for (int i = 0; i < gameData.getEventCount(); i++) {
127-
ClientData.Event event = gameData.getEvents(i);
128-
EventHandler.operation(eventListener, game, event);
129-
if (event.getType() == EventType.MatchEnd) {
130-
gameOver = true;
137+
performanceMetrics.botResponse.time(() -> {
138+
ClientData.GameData gameData = game.clientData().gameData();
139+
for (int i = 0; i < gameData.getEventCount(); i++) {
140+
ClientData.Event event = gameData.getEvents(i);
141+
EventHandler.operation(eventListener, game, event);
142+
if (event.getType() == EventType.MatchEnd) {
143+
gameOver = true;
144+
}
131145
}
132-
}
146+
});
133147
}
134148
}

src/main/java/bwapi/Bullet.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* represents a new Bullet.
2222
* <p>
2323
* If {@link Flag#CompleteMapInformation} is disabled, then a {@link Bullet} is accessible if and only if
24-
* it is visible. Otherwise if {@link Flag#CompleteMapInformation} is enabled, then all Bullets
24+
* it is visible. Otherwise if {@link Flag#CompleteMapInformation} is timersEnabled, then all Bullets
2525
* in the game are accessible.
2626
*
2727
* @see Game#getBullets
@@ -54,7 +54,7 @@ public int getID() {
5454
* return value will be false regardless of the Bullet's true existence. This is because
5555
* absolutely no state information on invisible enemy bullets is made available to the AI.
5656
* <p>
57-
* If {@link Flag#CompleteMapInformation} is enabled, then this function is accurate for all
57+
* If {@link Flag#CompleteMapInformation} is timersEnabled, then this function is accurate for all
5858
* {@link Bullet} information.
5959
*
6060
* @return true if the bullet exists or is visible, false if the bullet was destroyed or has gone out of scope.

src/main/java/bwapi/CommandTemp.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
/**
44
* Latency Compensation:
5-
* Only need to implement LatCom for current frame, the server updates the next frame already if latcom is enabled.
5+
* Only need to implement LatCom for current frame, the server updates the next frame already if latcom is timersEnabled.
66
* Use Caches for all internal state that might be affected by latcom, and add the (current) frame, to let Player & Unit
77
* check if they need to use the cached/latcom version of the value or the from server (or a combination of both)
88
* <p>

src/main/java/bwapi/FrameBuffer.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ of this software and associated documentation files (the "Software"), to deal
3737
class FrameBuffer {
3838

3939
private ByteBuffer dataSource;
40+
private PerformanceMetrics performanceMetrics;
4041
private int size;
4142
private int stepGame = 0;
4243
private int stepBot = 0;
@@ -57,8 +58,9 @@ class FrameBuffer {
5758
/**
5859
* Resets for a new game
5960
*/
60-
void initialize(ByteBuffer dataSource) {
61+
void initialize(ByteBuffer dataSource, PerformanceMetrics performanceMetrics) {
6162
this.dataSource = dataSource;
63+
this.performanceMetrics = performanceMetrics;
6264
stepGame = 0;
6365
stepBot = 0;
6466
}
@@ -110,7 +112,13 @@ void enqueueFrame() {
110112
lockWrite.lock();
111113
try {
112114
lockSize.lock();
113-
try { while (full()) conditionSize.awaitUninterruptibly(); } finally { lockSize.unlock(); };
115+
try {
116+
while (full()) {
117+
performanceMetrics.intentionallyBlocking.startTiming();
118+
conditionSize.awaitUninterruptibly();
119+
}
120+
performanceMetrics.intentionallyBlocking.stopTiming();
121+
} finally { lockSize.unlock(); };
114122
ByteBuffer dataTarget = dataBuffer.get(indexGame());
115123
dataSource.rewind();
116124
dataTarget.rewind();
@@ -133,7 +141,6 @@ ByteBuffer peek() {
133141
while(empty()) conditionSize.awaitUninterruptibly();
134142
return dataBuffer.get(indexBot());
135143
} finally { lockSize.unlock(); }
136-
137144
}
138145

139146
/**

src/main/java/bwapi/Game.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ public List<Player> getPlayers() {
329329

330330
/**
331331
* Retrieves the set of all accessible units.
332-
* If {@link Flag#CompleteMapInformation} is enabled, then the set also includes units that are not
332+
* If {@link Flag#CompleteMapInformation} is timersEnabled, then the set also includes units that are not
333333
* visible to the player.
334334
* <p>
335335
* Units that are inside refineries are not included in this set.
@@ -624,13 +624,13 @@ public void pingMinimap(final Position p) {
624624
}
625625

626626
/**
627-
* Checks if the state of the given flag is enabled or not.
627+
* Checks if the state of the given flag is timersEnabled or not.
628628
* <p>
629-
* Flags may only be enabled at the start of the match during the {@link BWEventListener#onStart}
629+
* Flags may only be timersEnabled at the start of the match during the {@link BWEventListener#onStart}
630630
* callback.
631631
*
632632
* @param flag The {@link Flag} entry describing the flag's effects on BWAPI.
633-
* @return true if the given flag is enabled, false if the flag is disabled.
633+
* @return true if the given flag is timersEnabled, false if the flag is disabled.
634634
* @see Flag
635635
*/
636636
public boolean isFlagEnabled(final Flag flag) {
@@ -640,7 +640,7 @@ public boolean isFlagEnabled(final Flag flag) {
640640
/**
641641
* Enables the state of a given flag.
642642
* <p>
643-
* Flags may only be enabled at the start of the match during the {@link BWEventListener#onStart}
643+
* Flags may only be timersEnabled at the start of the match during the {@link BWEventListener#onStart}
644644
* callback.
645645
*
646646
* @param flag The {@link Flag} entry describing the flag's effects on BWAPI.
@@ -1640,7 +1640,7 @@ public boolean issueCommand(final Collection<Unit> units, final UnitCommand comm
16401640

16411641
/**
16421642
* Retrieves the set of units that are currently selected by the user outside of
1643-
* BWAPI. This function requires that{@link Flag#UserInput} be enabled.
1643+
* BWAPI. This function requires that{@link Flag#UserInput} be timersEnabled.
16441644
*
16451645
* @return A List<Unit> containing the user's selected units. If {@link Flag#UserInput} is disabled,
16461646
* then this set is always empty.
@@ -2163,7 +2163,7 @@ public boolean isDebug() {
21632163
/**
21642164
* Checks the state of latency compensation.
21652165
*
2166-
* @return true if latency compensation is enabled, false if it is disabled.
2166+
* @return true if latency compensation is timersEnabled, false if it is disabled.
21672167
* @see #setLatCom
21682168
*/
21692169
public boolean isLatComEnabled() {
@@ -2174,9 +2174,9 @@ public boolean isLatComEnabled() {
21742174
* Changes the state of latency compensation. Latency compensation
21752175
* modifies the state of BWAPI's representation of units to reflect the implications of
21762176
* issuing a command immediately after the command was performed, instead of waiting
2177-
* consecutive frames for the results. Latency compensation is enabled by default.
2177+
* consecutive frames for the results. Latency compensation is timersEnabled by default.
21782178
*
2179-
* @param isEnabled Set whether the latency compensation feature will be enabled (true) or disabled (false).
2179+
* @param isEnabled Set whether the latency compensation feature will be timersEnabled (true) or disabled (false).
21802180
* @see #isLatComEnabled
21812181
*/
21822182
public void setLatCom(final boolean isEnabled) {
@@ -2278,11 +2278,11 @@ public boolean setVision(Player player, boolean enabled) {
22782278
}
22792279

22802280
/**
2281-
* Checks if the GUI is enabled.
2281+
* Checks if the GUI is timersEnabled.
22822282
* <p>
22832283
* The GUI includes all drawing functions of BWAPI, as well as screen updates from Broodwar.
22842284
*
2285-
* @return true if the GUI is enabled, and everything is visible, false if the GUI is disabled and drawing
2285+
* @return true if the GUI is timersEnabled, and everything is visible, false if the GUI is disabled and drawing
22862286
* functions are rejected
22872287
* @see #setGUI
22882288
*/
@@ -2341,7 +2341,7 @@ public boolean setMap(final String mapFileName) {
23412341
/**
23422342
* Sets the state of the fog of war when watching a replay.
23432343
*
2344-
* @param reveal The state of the reveal all flag. If false, all fog of war will be enabled. If true,
2344+
* @param reveal The state of the reveal all flag. If false, all fog of war will be timersEnabled. If true,
23452345
* then the fog of war will be revealed. It is true by default.
23462346
*/
23472347
public boolean setRevealAll(boolean reveal) {

0 commit comments

Comments
 (0)