Skip to content

Commit 5112ffe

Browse files
committed
chore: optimize writer I/O with batch flush and reduce allocations
1 parent 893f10f commit 5112ffe

9 files changed

Lines changed: 86 additions & 37 deletions

File tree

src/main/java/org/damon233/performtrackermod/controller/TrackerController.java

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
import org.damon233.performtrackermod.PerformTracker;
1616
import org.damon233.performtrackermod.collector.ServerMetricsCollector;
17-
import org.damon233.performtrackermod.collector.SystemInfoCollector;
1817
import org.damon233.performtrackermod.collector.IFpsProvider;
1918
import org.damon233.performtrackermod.config.ConfigAccess;
2019
import org.damon233.performtrackermod.data.PerformanceMetrics;
@@ -44,6 +43,9 @@ public class TrackerController {
4443
private MinecraftServer server;
4544
private String currentOutputFormat;
4645

46+
private final Object[] rowValuesBuffer = new Object[6];
47+
private final StringBuilder chatMessageBuilder = new StringBuilder(256);
48+
4749
public TrackerController(ServerMetricsCollector serverCollector, IFpsProvider fpsProvider) {
4850
this.serverCollector = serverCollector;
4951
this.fpsProvider = fpsProvider;
@@ -222,47 +224,46 @@ private void outputMetrics() {
222224
}
223225

224226
private String buildChatMessage(PerformanceMetrics metrics) {
225-
StringBuilder sb = new StringBuilder();
227+
chatMessageBuilder.setLength(0);
226228
boolean first = true;
227229
if (ConfigAccess.isCollectFps()) {
228-
if (!first) sb.append(" | ");
229-
sb.append(TranslationService.colorLabel("FPS: ")).append(TranslationService.colorValue(String.format("%.1f", metrics.fps())));
230+
if (!first) chatMessageBuilder.append(" | ");
231+
chatMessageBuilder.append(TranslationService.colorLabel("FPS: ")).append(TranslationService.colorValue(String.format("%.1f", metrics.fps())));
230232
first = false;
231233
}
232234
if (ConfigAccess.isCollectTps()) {
233-
if (!first) sb.append(" | ");
234-
sb.append(TranslationService.colorLabel("TPS: ")).append(TranslationService.colorValue(PerformanceMetrics.formatValue(metrics.tps())));
235+
if (!first) chatMessageBuilder.append(" | ");
236+
chatMessageBuilder.append(TranslationService.colorLabel("TPS: ")).append(TranslationService.colorValue(PerformanceMetrics.formatValue(metrics.tps())));
235237
first = false;
236238
}
237239
if (ConfigAccess.isCollectMspt()) {
238-
if (!first) sb.append(" | ");
239-
sb.append(TranslationService.colorLabel("MSPT: ")).append(TranslationService.colorValue(PerformanceMetrics.formatValue(metrics.mspt())));
240+
if (!first) chatMessageBuilder.append(" | ");
241+
chatMessageBuilder.append(TranslationService.colorLabel("MSPT: ")).append(TranslationService.colorValue(PerformanceMetrics.formatValue(metrics.mspt())));
240242
first = false;
241243
}
242244
if (ConfigAccess.isCollectHeap()) {
243-
if (!first) sb.append(" | ");
244-
sb.append(TranslationService.colorLabel("Heap: "))
245+
if (!first) chatMessageBuilder.append(" | ");
246+
chatMessageBuilder.append(TranslationService.colorLabel("Heap: "))
245247
.append(TranslationService.colorValue(PerformanceMetrics.formatMemoryMB(metrics.heapUsed())))
246248
.append(" / ")
247249
.append(TranslationService.colorValue(PerformanceMetrics.formatMemoryMB(metrics.heapMax())));
248250
}
249251
if (ConfigAccess.isCollectCpu()) {
250-
if (!first) sb.append(" | ");
251-
sb.append(TranslationService.colorLabel("CPU: "))
252+
if (!first) chatMessageBuilder.append(" | ");
253+
chatMessageBuilder.append(TranslationService.colorLabel("CPU: "))
252254
.append(TranslationService.colorValue(String.format("%.1f%%", metrics.cpuUsage())));
253255
}
254-
return sb.toString();
256+
return chatMessageBuilder.toString();
255257
}
256-
258+
257259
private Object[] buildRowValues(PerformanceMetrics metrics) {
258-
return new Object[]{
259-
ConfigAccess.isCollectFps() ? metrics.fps() : Double.NaN,
260-
ConfigAccess.isCollectTps() ? metrics.tps() : Double.NaN,
261-
ConfigAccess.isCollectMspt() ? metrics.mspt() : Double.NaN,
262-
ConfigAccess.isCollectHeap() ? metrics.heapUsed() : Double.NaN,
263-
ConfigAccess.isCollectHeap() ? metrics.heapMax() : Double.NaN,
264-
ConfigAccess.isCollectCpu() ? metrics.cpuUsage() : Double.NaN
265-
};
260+
rowValuesBuffer[0] = ConfigAccess.isCollectFps() ? metrics.fps() : Double.NaN;
261+
rowValuesBuffer[1] = ConfigAccess.isCollectTps() ? metrics.tps() : Double.NaN;
262+
rowValuesBuffer[2] = ConfigAccess.isCollectMspt() ? metrics.mspt() : Double.NaN;
263+
rowValuesBuffer[3] = ConfigAccess.isCollectHeap() ? metrics.heapUsed() : Double.NaN;
264+
rowValuesBuffer[4] = ConfigAccess.isCollectHeap() ? metrics.heapMax() : Double.NaN;
265+
rowValuesBuffer[5] = ConfigAccess.isCollectCpu() ? metrics.cpuUsage() : Double.NaN;
266+
return rowValuesBuffer;
266267
}
267268

268269
public PerformanceMetrics getMetrics() {

src/main/java/org/damon233/performtrackermod/writer/CsvWriter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ protected void writeRowInternal(Object... values) throws IOException {
5151
writer.newLine();
5252
}
5353

54+
@Override
55+
protected void flushInternal() throws IOException {
56+
if (writer != null) {
57+
writer.flush();
58+
}
59+
}
60+
5461
@Override
5562
protected void onClose() throws IOException {
5663
if (writer != null) {

src/main/java/org/damon233/performtrackermod/writer/JsonWriter.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,13 @@ protected synchronized void writeRowInternal(Object... values) throws IOExceptio
6161

6262
writer.write(" ");
6363
writer.write(GSON.toJson(row));
64-
writer.flush();
64+
}
65+
66+
@Override
67+
protected void flushInternal() throws IOException {
68+
if (writer != null) {
69+
writer.flush();
70+
}
6571
}
6672

6773
private Object formatValue(Object v) {
@@ -79,6 +85,7 @@ protected void onClose() throws IOException {
7985
if (headerWritten) {
8086
writer.write("\n]");
8187
}
88+
writer.flush();
8289
writer.close();
8390
writer = null;
8491
}

src/main/java/org/damon233/performtrackermod/writer/MetricsWriter.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import java.io.IOException;
44
import java.nio.file.Files;
55
import java.nio.file.Path;
6-
import java.nio.file.StandardOpenOption;
76
import java.time.Instant;
87
import java.time.ZoneId;
98
import java.time.format.DateTimeFormatter;
@@ -20,6 +19,8 @@
2019
public abstract class MetricsWriter implements AutoCloseable {
2120
private static final Logger LOGGER = LoggerFactory.getLogger("performtracker");
2221
private static final int MAX_QUEUE_SIZE = 100;
22+
private static final int FLUSH_INTERVAL_ROWS = 100;
23+
private static final long FLUSH_INTERVAL_MS = 30_000;
2324
protected static final DateTimeFormatter FILENAME_FORMAT =
2425
DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss").withZone(ZoneId.systemDefault());
2526

@@ -28,6 +29,9 @@ public abstract class MetricsWriter implements AutoCloseable {
2829
protected final AtomicBoolean running;
2930
protected final Path filePath;
3031

32+
private int rowCount;
33+
private long lastFlushTime;
34+
3135
protected MetricsWriter(String directory, String baseName, String extension) throws IOException {
3236
this.writeQueue = new LinkedBlockingQueue<>(MAX_QUEUE_SIZE);
3337
this.executor = Executors.newSingleThreadExecutor(r -> {
@@ -39,6 +43,8 @@ protected MetricsWriter(String directory, String baseName, String extension) thr
3943
String filename = String.format("%s_%s.%s", baseName, FILENAME_FORMAT.format(Instant.now()), extension);
4044
this.filePath = Path.of(directory, filename);
4145
Files.createDirectories(Path.of(directory));
46+
this.rowCount = 0;
47+
this.lastFlushTime = System.currentTimeMillis();
4248
}
4349

4450
public Path getFilePath() {
@@ -67,12 +73,26 @@ public void enqueue(Object... values) {
6773

6874
protected abstract void writeRowInternal(Object... values) throws IOException;
6975

76+
protected abstract void flushInternal() throws IOException;
77+
7078
protected void writeLoop() {
7179
while (running.get()) {
7280
try {
7381
Object[] values = writeQueue.poll(1, TimeUnit.SECONDS);
7482
if (values != null) {
7583
writeRowInternal(values);
84+
rowCount++;
85+
if (rowCount >= FLUSH_INTERVAL_ROWS || System.currentTimeMillis() - lastFlushTime >= FLUSH_INTERVAL_MS) {
86+
flushInternal();
87+
rowCount = 0;
88+
lastFlushTime = System.currentTimeMillis();
89+
}
90+
} else {
91+
if (rowCount > 0 && System.currentTimeMillis() - lastFlushTime >= FLUSH_INTERVAL_MS) {
92+
flushInternal();
93+
rowCount = 0;
94+
lastFlushTime = System.currentTimeMillis();
95+
}
7696
}
7797
} catch (InterruptedException e) {
7898
Thread.currentThread().interrupt();
@@ -84,6 +104,13 @@ protected void writeLoop() {
84104
}
85105

86106
public void flush() {
107+
try {
108+
flushInternal();
109+
rowCount = 0;
110+
lastFlushTime = System.currentTimeMillis();
111+
} catch (IOException e) {
112+
LOGGER.error("Failed to flush writer", e);
113+
}
87114
}
88115

89116
@Override

src/main/java/org/damon233/performtrackermod/writer/YamlWriter.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,13 @@ protected synchronized void writeRowInternal(Object... values) throws IOExceptio
4747
Object v = values[i];
4848
writer.write(String.format(" %s: %s\n", key, formatValue(v)));
4949
}
50-
writer.flush();
50+
}
51+
52+
@Override
53+
protected void flushInternal() throws IOException {
54+
if (writer != null) {
55+
writer.flush();
56+
}
5157
}
5258

5359
private String formatValue(Object v) {
@@ -73,6 +79,7 @@ private String formatValue(Object v) {
7379
@Override
7480
protected void onClose() throws IOException {
7581
if (writer != null) {
82+
writer.flush();
7683
writer.close();
7784
writer = null;
7885
}

src/main/resources/assets/performtracker/lang/en_us.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
"performtracker.stop.success": "Performance tracking stopped. %d samples collected.",
66
"performtracker.error.not_initialized": "Tracker not initialized\nPerhaps because you haven't enabled any metrics. Enable at least one in settings.",
77
"performtracker.error.already_running": "Tracker is already running",
8-
"performtracker.error.not_running": "Tracker is not running",
9-
"performtracker.error.http_server_failed": "Failed to start HTTP server on port %d. Network features will be disabled.",
10-
"performtracker.error.chip_rules_modified": "Chip classification rules have been modified. Device detection may be unreliable.",
8+
"performtracker.error.not_running": "Tracker is not running",
9+
"performtracker.error.http_server_failed": "Failed to start HTTP server on port %d. Network features will be disabled.",
10+
"performtracker.error.chip_rules_modified": "Chip classification rules have been modified. Device detection may be unreliable.",
1111
"performtracker.command.start": "start",
1212
"performtracker.command.stop": "stop",
1313
"performtracker.command.description": "Performance tracking commands",

src/main/resources/assets/performtracker/lang/lzh.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
"performtracker.stop.success": "效能追蹤既訖, 凡集樣 %d 枚。",
66
"performtracker.error.not_initialized": "追蹤器未始\n恐未啟度數, 望君於置設啟其一。",
77
"performtracker.error.already_running": "追蹤器方行中",
8-
"performtracker.error.not_running": "追蹤器未運",
9-
"performtracker.error.http_server_failed": "HTTP 伺器之端口 %d 敗於啟。網傳之術止矣。",
10-
"performtracker.error.chip_rules_modified": "算牒分類之法已改。器械鑒定恐失其準。",
8+
"performtracker.error.not_running": "追蹤器未運",
9+
"performtracker.error.http_server_failed": "HTTP 伺器之端口 %d 敗於啟。網傳之術止矣。",
10+
"performtracker.error.chip_rules_modified": "算牒分類之法已改。器械鑒定恐失其準。",
1111
"performtracker.command.start": "",
1212
"performtracker.command.stop": "",
1313
"performtracker.command.description": "效能追蹤令",

src/main/resources/assets/performtracker/lang/zh_cn.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
"performtracker.stop.success": "性能追踪已停止。共收集 %d 个样本。",
66
"performtracker.error.not_initialized": "追踪器未初始化\n可能是因为未启用任何指标。请在设置中至少启用一项指标。",
77
"performtracker.error.already_running": "追踪器已在运行中",
8-
"performtracker.error.not_running": "追踪器未在运行",
9-
"performtracker.error.http_server_failed": "HTTP 服务器在端口 %d 启动失败。网络功能将被禁用。",
10-
"performtracker.error.chip_rules_modified": "芯片分类规则已被修改。设备检测可能不可靠。",
8+
"performtracker.error.not_running": "追踪器未在运行",
9+
"performtracker.error.http_server_failed": "HTTP 服务器在端口 %d 启动失败。网络功能将被禁用。",
10+
"performtracker.error.chip_rules_modified": "芯片分类规则已被修改。设备检测可能不可靠。",
1111
"performtracker.command.start": "启动",
1212
"performtracker.command.stop": "停止",
1313
"performtracker.command.description": "性能追踪命令",

src/main/resources/assets/performtracker/lang/zh_tw.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
"performtracker.stop.success": "效能追蹤已停止。共收集 %d 筆樣本。",
66
"performtracker.error.not_initialized": "追蹤器未初始化\n或因为未啟用任何指標。請在設定中至少啟用一項指標。",
77
"performtracker.error.already_running": "追蹤器已在執行中",
8-
"performtracker.error.not_running": "追蹤器未在執行",
9-
"performtracker.error.http_server_failed": "HTTP 伺服器在連接埠 %d 啟動失敗。網路功能將被停用。",
10-
"performtracker.error.chip_rules_modified": "晶片分類規則已遭修改。設備偵測可能不可靠。",
8+
"performtracker.error.not_running": "追蹤器未在執行",
9+
"performtracker.error.http_server_failed": "HTTP 伺服器在連接埠 %d 啟動失敗。網路功能將被停用。",
10+
"performtracker.error.chip_rules_modified": "晶片分類規則已遭修改。設備偵測可能不可靠。",
1111
"performtracker.command.start": "啟動",
1212
"performtracker.command.stop": "停止",
1313
"performtracker.command.description": "效能追蹤指令",

0 commit comments

Comments
 (0)