|
1 | 1 | package com.techatpark.sjson; |
2 | 2 |
|
3 | | -import com.fasterxml.jackson.databind.JsonNode; |
4 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; |
5 | | -import com.google.gson.JsonElement; |
| 4 | +import com.google.gson.Gson; |
6 | 5 | import com.google.gson.JsonParser; |
7 | 6 | import com.techatpark.sjson.util.TestDataProvider; |
8 | 7 | import org.github.jamm.MemoryMeter; |
9 | 8 | import org.json.JSONObject; |
| 9 | +import org.junit.jupiter.api.AfterAll; |
10 | 10 | import org.junit.jupiter.api.Assertions; |
| 11 | +import org.junit.jupiter.api.TestInstance; |
11 | 12 | import org.junit.jupiter.params.ParameterizedTest; |
12 | 13 | import org.junit.jupiter.params.provider.MethodSource; |
13 | 14 |
|
|
16 | 17 | import java.io.IOException; |
17 | 18 | import java.io.StringReader; |
18 | 19 | import java.nio.file.Path; |
| 20 | +import java.util.Map; |
19 | 21 | import java.util.Set; |
20 | 22 |
|
21 | | - |
| 23 | +/** |
| 24 | + * Reads Json Objects as Map<String,Object> using various parsers, |
| 25 | + * Compare them with Json.parse() by Memory usage and performance |
| 26 | + */ |
| 27 | +@TestInstance(TestInstance.Lifecycle.PER_CLASS) |
22 | 28 | class PerformanceTest { |
23 | 29 |
|
24 | | - public static final String ANSI_RESET = "\u001B[0m"; |
25 | | - public static final String ANSI_RED = "\u001B[31m"; |
26 | | - public static final String ANSI_GREEN = "\u001B[32m"; |
27 | | - public static final String ANSI_WHITE = "\u001B[37m"; |
| 30 | + private static final String ANSI_RESET = "\u001B[0m"; |
| 31 | + private static final String ANSI_RED = "\u001B[31m"; |
| 32 | + private static final String ANSI_GREEN = "\u001B[32m"; |
| 33 | + private static final String ANSI_WHITE = "\u001B[37m"; |
| 34 | + |
| 35 | + private final ObjectMapper jackson = new ObjectMapper(); |
| 36 | + private final Gson gson = new Gson(); |
| 37 | + |
| 38 | + private final MemoryMeter meter = MemoryMeter.builder().build(); |
28 | 39 |
|
| 40 | + long totalOursTime = 0; |
| 41 | + long totalOrgJsonTime = 0; |
| 42 | + long totalJacksonTime = 0; |
| 43 | + long totalGsonTime = 0; |
29 | 44 |
|
30 | | - public final ObjectMapper jackson = new ObjectMapper(); |
| 45 | + long totalOursSize = 0; |
| 46 | + long totalOrgJsonSize = 0; |
| 47 | + long totalJacksonSize = 0; |
| 48 | + long totalGsonSize = 0; |
| 49 | + |
| 50 | + int fileCount = 0; |
| 51 | + |
31 | 52 |
|
32 | 53 | @ParameterizedTest |
33 | 54 | @MethodSource("jsonFilesProvider") |
34 | 55 | void testRead(Path path) throws IOException { |
35 | | - MemoryMeter meter = MemoryMeter.builder().build(); |
36 | | - Object ourJsonObject; |
37 | | - JSONObject orgJSONObject; |
38 | | - JsonNode jacksonJsonNode; |
39 | | - JsonElement gsonObject; |
40 | | - |
41 | | - long start; |
| 56 | + fileCount++; |
42 | 57 |
|
43 | | - long jacksonsTime, jsonTime, gsonTime, oursTime; |
44 | | - long jacksonsSize, jsonSize, gsonSize, oursSize; |
| 58 | + // Our Json |
| 59 | + long start = System.nanoTime(); |
| 60 | + Object ourJsonObject = Json.read(new BufferedReader(new FileReader(path.toFile()))); |
| 61 | + long oursTime = System.nanoTime() - start; |
| 62 | + long oursSize = meter.measureDeep(ourJsonObject); |
45 | 63 |
|
46 | | - // Measure SJson |
| 64 | + // Org JSON |
47 | 65 | start = System.nanoTime(); |
48 | | - ourJsonObject = Json.read(new BufferedReader(new FileReader(path.toFile()))); |
49 | | - oursTime = System.nanoTime() - start; |
50 | | - oursSize = meter.measureDeep(ourJsonObject); |
| 66 | + Map orgJsonObject = new JSONObject(new BufferedReader(new FileReader(path.toFile()))).toMap(); |
| 67 | + long orgjsonTime = System.nanoTime() - start; |
| 68 | + long orgjsonSize = meter.measureDeep(orgJsonObject); |
51 | 69 |
|
52 | | - // Measure Org Json |
| 70 | + // Jackson |
53 | 71 | start = System.nanoTime(); |
54 | | - orgJSONObject = new JSONObject(new BufferedReader(new FileReader(path.toFile()))); |
55 | | - jsonTime = System.nanoTime() - start - oursTime; |
56 | | - jsonSize = meter.measureDeep(orgJSONObject); |
| 72 | + Map jacksonJson = jackson.readValue(new BufferedReader(new FileReader(path.toFile())), Map.class); |
| 73 | + long jacksonsTime = System.nanoTime() - start; |
| 74 | + long jacksonsSize = meter.measureDeep(jacksonJson); |
57 | 75 |
|
58 | | - // Measure Jackson |
| 76 | + // Gson |
59 | 77 | start = System.nanoTime(); |
60 | | - jacksonJsonNode = jackson.readTree(new BufferedReader(new FileReader(path.toFile()))); |
61 | | - jacksonsTime = System.nanoTime() - start - oursTime; |
62 | | - jacksonsSize = meter.measureDeep(jacksonJsonNode); |
| 78 | + Map gsonJson = gson.fromJson(new BufferedReader(new FileReader(path.toFile())), Map.class); |
| 79 | + long gsonTime = System.nanoTime() - start; |
| 80 | + long gsonSize = meter.measureDeep(gsonJson); |
63 | 81 |
|
64 | | - // Measure Gson |
65 | | - start = System.nanoTime(); |
66 | | - gsonObject = JsonParser.parseReader(new BufferedReader(new FileReader(path.toFile()))); |
67 | | - gsonTime = System.nanoTime() - start - oursTime; |
68 | | - gsonSize = meter.measureDeep(gsonObject); |
| 82 | + // Accumulate totals |
| 83 | + totalOursTime += oursTime; |
| 84 | + totalOrgJsonTime += orgjsonTime; |
| 85 | + totalJacksonTime += jacksonsTime; |
| 86 | + totalGsonTime += gsonTime; |
| 87 | + |
| 88 | + totalOursSize += oursSize; |
| 89 | + totalOrgJsonSize += orgjsonSize; |
| 90 | + totalJacksonSize += jacksonsSize; |
| 91 | + totalGsonSize += gsonSize; |
69 | 92 |
|
70 | 93 | // Check Correctness of the parser |
71 | | - Assertions.assertEquals(gsonObject, |
| 94 | + Assertions.assertEquals(JsonParser.parseReader(new StringReader(jackson.writeValueAsString(gsonJson))), |
72 | 95 | JsonParser.parseReader(new StringReader(jackson.writeValueAsString(ourJsonObject))), |
73 | 96 | "Reverse JSON Failed for " + path); |
74 | 97 |
|
| 98 | + // Print formatted table |
75 | 99 | System.out.format("%33s%20s%20s%20s%10s%20s%20s%20s\n", |
76 | 100 | ANSI_RESET + path.getFileName(), |
77 | | - getSizeDisplay(jsonSize, oursSize), |
78 | | - getSizeDisplay(jacksonsSize, oursSize), |
79 | | - getSizeDisplay(gsonSize, oursSize), |
| 101 | + getMemoryDisplay(orgjsonSize, oursSize), |
| 102 | + getMemoryDisplay(jacksonsSize, oursSize), |
| 103 | + getMemoryDisplay(gsonSize, oursSize), |
| 104 | + ANSI_WHITE + "|", |
| 105 | + getTimeDisplay(orgjsonTime, oursTime), |
| 106 | + getTimeDisplay(jacksonsTime, oursTime), |
| 107 | + getTimeDisplay(gsonTime, oursTime) |
| 108 | + ); |
| 109 | + } |
| 110 | + |
| 111 | + @AfterAll |
| 112 | + void printAverages() { |
| 113 | + System.out.println(ANSI_RESET + "\nAverage Memory and Time Usage (nanoseconds and bytes):"); |
| 114 | + System.out.format("%33s%20s%20s%20s%10s%20s%20s%20s\n", |
| 115 | + ANSI_RESET + "Average", |
| 116 | + getMemoryDisplay(totalOrgJsonSize / fileCount, totalOursSize / fileCount), |
| 117 | + getMemoryDisplay(totalJacksonSize / fileCount, totalOursSize / fileCount), |
| 118 | + getMemoryDisplay(totalGsonSize / fileCount, totalOursSize / fileCount), |
80 | 119 | ANSI_WHITE + "|", |
81 | | - getTimeDisplay(jsonTime), |
82 | | - getTimeDisplay(jacksonsTime), |
83 | | - getTimeDisplay(gsonTime) |
| 120 | + getTimeDisplay(totalOrgJsonTime / fileCount, totalOursTime / fileCount), |
| 121 | + getTimeDisplay(totalJacksonTime / fileCount, totalOursTime / fileCount), |
| 122 | + getTimeDisplay(totalGsonTime / fileCount, totalOursTime / fileCount) |
84 | 123 | ); |
85 | 124 | } |
86 | 125 |
|
87 | | - /** |
88 | | - * Provides paths to JSON files for parameterized tests. |
89 | | - * |
90 | | - * @return Stream of paths to JSON files |
91 | | - * @throws IOException if there is an issue listing files |
92 | | - */ |
93 | 126 | private static Set<Path> jsonFilesProvider() throws IOException { |
94 | 127 | return TestDataProvider.getJSONObjectFiles(); |
95 | 128 | } |
96 | 129 |
|
97 | | - /** |
98 | | - * Get Timing in a Color Coded Format. |
99 | | - * @param time |
100 | | - * @return time as text |
101 | | - */ |
102 | | - private String getTimeDisplay(final long time) { |
| 130 | + private String getTimeDisplay(final long time, final long oursTime) { |
103 | 131 | StringBuilder builder = new StringBuilder(); |
104 | | - if(time < 0) { |
105 | | - builder.append(ANSI_RED); |
106 | | - } |
107 | | - else { |
| 132 | + long gap = time - oursTime; |
| 133 | + if (gap > 0) { |
108 | 134 | builder.append(ANSI_GREEN); |
| 135 | + } else { |
| 136 | + builder.append(ANSI_RED); |
109 | 137 | } |
110 | 138 | return builder.append(time).toString(); |
111 | 139 | } |
112 | 140 |
|
113 | | - /** |
114 | | - * Get Size in a Color Coded Format. |
115 | | - * @param size |
116 | | - * @return size as text |
117 | | - */ |
118 | | - private String getSizeDisplay(final long size,final long ourSize) { |
| 141 | + private String getMemoryDisplay(final long size, final long ourSize) { |
119 | 142 | StringBuilder builder = new StringBuilder(); |
120 | | - long gap = size-ourSize; |
121 | | - if(gap < 0) { |
122 | | - builder.append(ANSI_RED); |
123 | | - } |
124 | | - else { |
| 143 | + if (size > ourSize) { |
125 | 144 | builder.append(ANSI_GREEN); |
| 145 | + } else { |
| 146 | + builder.append(ANSI_RED); |
126 | 147 | } |
127 | | - return builder.append(gap).toString(); |
| 148 | + return builder.append(size).toString(); |
128 | 149 | } |
129 | 150 | } |
0 commit comments