Skip to content

Commit

Permalink
Optimize simple strings for +OK\r\n responses
Browse files Browse the repository at this point in the history
Previously, `redis.set "foo", "bar"` allocated 16 bytes of heap memory
for the "OK" response from the server. With this commit, it doesn't
allocate any heap memory at all.
  • Loading branch information
jgaskins committed Dec 23, 2024
1 parent efb5e6c commit aafe485
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 4 deletions.
9 changes: 7 additions & 2 deletions spec/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ module Redis
end

it "reads simple strings" do
io = IO::Memory.new("+OK\r\n")
Parser.new(io).read.should eq "OK"
io = IO::Memory.new("+OK\r\n+QUEUED\r\n+OK\r\n+QUEUED\r\n")
parser = Parser.new(io)

2.times do
parser.read.should eq "OK"
parser.read.should eq "QUEUED"
end
end

it "reads bulk strings" do
Expand Down
2 changes: 1 addition & 1 deletion spec/redis_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ end
describe Redis::Client do
test "can set, get, and delete keys" do
redis.get(random_key).should eq nil
redis.set(key, "hello")
redis.set(key, "hello").should eq "OK"
redis.get(key).should eq "hello"
redis.del(key).should eq 1
redis.del(key).should eq 0
Expand Down
17 changes: 16 additions & 1 deletion src/parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,22 @@ module Redis
value
end
when '+'
@io.read_line
# Most of the time, RESP simple strings are just "OK", so we can
# optimize for that case to avoid heap allocations. If it is *not* the
# "OK" string, this does an extra heap allocation, but that seems like
# a decent tradeoff considering the vast majority of times a simple
# string will be returned from the server is from a SET call.
buffer = uninitialized UInt8[4] # "OK\r\n"
slice = buffer.to_slice
read = @io.read slice
result = if read == 4 && slice == "OK\r\n".to_slice
"OK"
else
String.build do |str|
str.write slice[0...read]
str << @io.read_line
end
end
when '-'
type, message = @io.read_line.split(' ', 2)
ERROR_MAP[type].new("#{type} #{message}")
Expand Down

0 comments on commit aafe485

Please sign in to comment.