arunkumarucet opened a new pull request, #17485: URL: https://github.com/apache/pinot/pull/17485
### Problem During profiling of high-throughput real-time ingestion, identified that JSON message decoding in `RealtimeSegmentDataManager.consumeLoop()` was consuming significant CPU cycles. The flame graph showed that `JsonUtils.bytesToJsonNode` and `JsonUtils.jsonNodeToMap` were hot paths during stream consumption. The current implementation uses a two-step JSON parsing approach: `// Step 1: Parse bytes to JsonNode (intermediate tree representation)` `JsonNode message = JsonUtils.bytesToJsonNode(payload, offset, length);` `// Step 2: Convert JsonNode to Map` `Map<String, Object> from = JsonUtils.jsonNodeToMap(message);` This is inefficient because: 1. It creates an intermediate JsonNode tree structure with node objects (ObjectNode, ArrayNode, TextNode, etc.) 2. It then traverses that tree again to extract values into a Map<String, Object> 3. The intermediate objects create additional memory allocations <img width="1722" height="484" alt="image" src="https://github.com/user-attachments/assets/f0eeef59-5799-4584-9eac-2a8c2bb981b6" /> ### Solution Added a new `JsonUtils.bytesToMap()` method that parses JSON bytes directly to Map<String, Object> in a single pass, skipping the intermediate JsonNode representation: `// New: Direct parsing to Map` `Map<String, Object> jsonMap = JsonUtils.bytesToMap(payload, offset, length);` This leverages Jackson's `ObjectReader.readValue()` method which can deserialize directly to a target type without building the tree model first. ### Unit Tests Added Comprehensive test coverage verifying functional equivalence between old and new approaches: - Equivalence tests: 45 different JSON payloads via `@DataProvider` comparing both approaches - Edge cases tested: - Empty objects/arrays - Null handling - All primitive types (int, long, float, double, boolean, string) - Large numbers (Long.MAX_VALUE, scientific notation) - Unicode/UTF-8 encoding (Chinese, Japanese, Korean, emoji) - Escape sequences (quotes, backslash, newline, tab, unicode escapes) - Nested structures (5+ levels deep) - Mixed-type arrays - Offset/length partial array parsing - Error handling (malformed JSON, incomplete JSON) - Stress tests (100 fields, deeply nested arrays) ### Benchmark Results Payload Type | Old Approach (ops/s) | New Approach (ops/s) | Improvement -- | -- | -- | -- small (~100 bytes) | 1,456,586 | 1,994,718 | +36.9% medium (~500 bytes) | 614,918 | 945,561 | +53.8% large (~2KB) | 185,895 | 257,569 | +38.6% nested (~1KB) | 316,107 | 525,349 | +66.2% ### Key findings: 1. 35-55% improvement on small to medium payloads (most common in streaming scenarios) 2. 35-40% improvement even on large 2KB payloads 3. Consistent improvement across all tested payload structures ### Real-World Impact For a Pinot server ingesting JSON messages from Kafka: 1. Reduced CPU usage in the JSON parsing hot path by ~35-50% 2. Lower GC pressure due to fewer intermediate object allocations 3. Higher sustainable ingestion throughput before CPU saturation -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
