perf: cache command string and add zero-allocation integer parsing#189
Merged
tonivade merged 3 commits intotonivade:masterfrom Mar 30, 2026
Merged
perf: cache command string and add zero-allocation integer parsing#189tonivade merged 3 commits intotonivade:masterfrom
tonivade merged 3 commits intotonivade:masterfrom
Conversation
added 2 commits
March 23, 2026 09:49
Two related allocation-reduction optimizations: 1. Cache the result of getCommand() in DefaultRequest to avoid repeated SafeString.toString() conversions. The command name is immutable per request, so lazy-init caching is safe. 2. Add SafeString.parseIntAfterPrefix() that parses the integer value directly from the underlying ByteBuffer, skipping the first byte (the RESP type prefix). This replaces the pattern of substring(1) + Integer.parseInt() in RedisParser, eliminating a temporary String allocation per parsed RESP header. Made-with: Cursor
- SafeStringTest: verify parseIntAfterPrefix() for positive, negative, zero, and large integers with various RESP prefixes - DefaultRequestTest: verify getCommand() returns same cached instance across calls, and isExit() correctly uses the cached value Made-with: Cursor
tonivade
requested changes
Mar 30, 2026
| for (int i = start; i < lim; i++) { | ||
| byte b = buffer.get(i); | ||
| if (b < '0' || b > '9') { | ||
| break; |
Owner
There was a problem hiding this comment.
this should throw a NumberFormatException, right? also this case should be verified in tests
src/main/java/com/github/tonivade/resp/command/DefaultRequest.java
Outdated
Show resolved
Hide resolved
Made-with: Cursor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
DefaultRequestto avoid repeatedSafeString.toString()conversions.SafeString.parseIntAfterPrefix()for zero-allocation integer parsing of RESP protocol headers, replacing thesubstring(1)+Integer.parseInt()pattern inRedisParser.Motivation
In the hot path of command processing:
DefaultRequest.getCommand()is called multiple times per request (dispatch, logging, exit check). Each call previously allocated a new String viaSafeString.toString().RedisParserparses every RESP frame header (*3,$5,:1) by first creating a substring (allocation), then parsing the integer.parseIntAfterPrefix()reads digits directly from the ByteBuffer — zero allocation.Changes
DefaultRequest.java: lazy-cachecommandStringfield; reuse inisExit()SafeString.java: newparseIntAfterPrefix()method — parses integer from buffer position+1, handles negative numbersRedisParser.java: useparseIntAfterPrefix()inparseToken(),parseIntegerToken(), andparseStringToken()Test plan