Part 6 extra: the decision table measured on real data
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
package com.ankurm.jackson3.part6streaming;
|
||||
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.core.JsonToken;
|
||||
import tools.jackson.core.json.JsonFactory;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BEYOND THE POST — the decision table, measured.
|
||||
*
|
||||
* The post ends with a table claiming data binding "loads full object", tree model
|
||||
* "loads full tree", and streaming uses "constant" memory. This generates a real
|
||||
* file and measures all three so the table has numbers behind it.
|
||||
*
|
||||
* These are indicative single-shot measurements on one JVM, not JMH benchmarks —
|
||||
* the ordering is the point, not the absolute figures.
|
||||
*/
|
||||
public class G04ThreeApproachesMeasured {
|
||||
|
||||
public record LogEntry(long id, String level, String message) { }
|
||||
|
||||
private static final int ENTRIES = 200_000;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
File file = generate();
|
||||
System.out.println("input file : " + (file.length() / 1024 / 1024) + " MB, "
|
||||
+ ENTRIES + " entries");
|
||||
System.out.println();
|
||||
|
||||
JsonMapper mapper = JsonMapper.builder().build();
|
||||
|
||||
measure("data binding (readValue)", () -> {
|
||||
List<LogEntry> all = mapper.readValue(file, new TypeReference<List<LogEntry>>() { });
|
||||
return all.stream().filter(e -> e.level().equals("ERROR")).count();
|
||||
});
|
||||
|
||||
measure("tree model (readTree)", () -> {
|
||||
JsonNode root = mapper.readTree(file);
|
||||
long n = 0;
|
||||
for (JsonNode node : root) if ("ERROR".equals(node.path("level").asString())) n++;
|
||||
return n;
|
||||
});
|
||||
|
||||
measure("streaming (JsonParser)", () -> {
|
||||
long n = 0;
|
||||
JsonFactory factory = new JsonFactory();
|
||||
try (JsonParser p = factory.createParser(tools.jackson.core.ObjectReadContext.empty(), file)) {
|
||||
p.nextToken();
|
||||
while (p.nextToken() != JsonToken.END_ARRAY) {
|
||||
String level = null;
|
||||
while (p.nextToken() != JsonToken.END_OBJECT) {
|
||||
String f = p.currentName();
|
||||
p.nextToken();
|
||||
if ("level".equals(f)) level = p.getString();
|
||||
}
|
||||
if ("ERROR".equals(level)) n++;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
});
|
||||
|
||||
file.delete();
|
||||
}
|
||||
|
||||
private interface Counter { long count() throws Exception; }
|
||||
|
||||
private static void measure(String label, Counter counter) throws Exception {
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
System.gc();
|
||||
Thread.sleep(120);
|
||||
long heapBefore = rt.totalMemory() - rt.freeMemory();
|
||||
long start = System.nanoTime();
|
||||
long errors = counter.count();
|
||||
long ms = (System.nanoTime() - start) / 1_000_000;
|
||||
long heapAfter = rt.totalMemory() - rt.freeMemory();
|
||||
System.out.printf("%-28s errors=%-6d %5d ms heap delta %5d MB%n",
|
||||
label, errors, ms, (heapAfter - heapBefore) / 1024 / 1024);
|
||||
}
|
||||
|
||||
private static File generate() throws Exception {
|
||||
File f = File.createTempFile("logs", ".json");
|
||||
f.deleteOnExit();
|
||||
StringBuilder sb = new StringBuilder("[");
|
||||
for (int i = 0; i < ENTRIES; i++) {
|
||||
if (i > 0) sb.append(',');
|
||||
sb.append("{\"id\":").append(i)
|
||||
.append(",\"level\":\"").append(i % 50 == 0 ? "ERROR" : "INFO")
|
||||
.append("\",\"message\":\"event number ").append(i)
|
||||
.append(" with some padding to make the payload realistic\"}");
|
||||
}
|
||||
sb.append(']');
|
||||
Files.writeString(f.toPath(), sb);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user