Skip to content

Instantly share code, notes, and snippets.

@jgaskins
Last active February 6, 2026 14:21
Show Gist options
  • Select an option

  • Save jgaskins/ecc0fc90d78cdec63c31e0ce5544faa1 to your computer and use it in GitHub Desktop.

Select an option

Save jgaskins/ecc0fc90d78cdec63c31e0ce5544faa1 to your computer and use it in GitHub Desktop.
Benchmarking redis gem vs hiredis vs custom Redis client
require 'bundler/inline'
require 'socket'
class MyRedis
CRLF = "\r\n"
def initialize
@connection = TCPSocket.new('localhost', 6379)
@connection.sync = false
end
def get(key)
@connection << "*2\r\n$3\r\nget\r\n$#{key.bytesize}\r\n#{key}\r\n"
@connection.flush
result = @connection.gets
if result.start_with?('$')
value = ''
bytesize = result.tap { |r| r[0] = '' }.to_i
return nil if bytesize < 0 # Key does not exist
@connection.read bytesize + 2, value
value.chomp! CRLF
value
else
raise "idk what to do with this: #{result.inspect}"
end
end
end
gemfile true do
source 'https://rubygems.org'
gem 'redis'
gem 'hiredis'
gem 'benchmark-ips'
end
require 'benchmark'
require 'benchmark/ips'
require 'redis'
require 'hiredis'
redis = Redis.new(driver: :ruby)
hiredis = Redis.new(driver: :hiredis)
myredis = MyRedis.new
key = "foo"
cpu_time_iterations = 100_000
# Quick functionality check on our custom Redis
redis.set key, "test"
if (myredis_value = myredis.get(key)) == (redis_value = redis.get(key))
puts "Cache-hit function check passed"
else
raise "Value mismatch. Redis: #{redis_value.inspect}, MyRedis: #{myredis_value.inspect}"
end
redis.del key
if myredis_value = myredis.get(key).nil?
puts "Cache-miss function check passed"
else
raise "MyRedis cache miss broken: #{myredis_value.inspect}"
end
puts
puts "Cache hit (returns a 2KB string)"
redis.set key, "." * 2048 # 2KB cache entry
Benchmark.ips do |x|
x.report("ruby") { redis.get key }
x.report("hiredis") { hiredis.get key }
x.report("myredis") { myredis.get key }
x.compare!
end
puts
puts "Cache hit CPU time"
puts "ruby : #{Benchmark.measure { cpu_time_iterations.times { redis.get key } }.total}"
puts "hiredis: #{Benchmark.measure { cpu_time_iterations.times { hiredis.get key } }.total}"
puts "myredis: #{Benchmark.measure { cpu_time_iterations.times { myredis.get key } }.total}"
puts
puts "Cache miss (returns nil)"
redis.del key
Benchmark.ips do |x|
x.report("ruby") { redis.get key }
x.report("hiredis") { hiredis.get key }
x.report("myredis") { myredis.get key }
x.compare!
end
puts
puts "Cache miss CPU time"
puts "ruby : #{Benchmark.measure { cpu_time_iterations.times { redis.get key } }.total}"
puts "hiredis: #{Benchmark.measure { cpu_time_iterations.times { hiredis.get key } }.total}"
puts "myredis: #{Benchmark.measure { cpu_time_iterations.times { myredis.get key } }.total}"
Fetching gem metadata from https://rubygems.org/....
Resolving dependencies...
Using benchmark-ips 2.8.4
Using bundler 2.2.3
Using hiredis 0.6.3
Using redis 4.2.5
Cache-hit function check passed
Cache-miss function check passed
Cache hit (returns a 2KB string)
Warming up --------------------------------------
ruby 1.857k i/100ms
hiredis 3.124k i/100ms
myredis 3.184k i/100ms
Calculating -------------------------------------
ruby 22.082k (±14.1%) i/s - 107.706k in 5.021179s
hiredis 31.506k (± 1.7%) i/s - 159.324k in 5.058568s
myredis 31.997k (± 2.8%) i/s - 162.384k in 5.079019s
Comparison:
myredis: 31997.4 i/s
hiredis: 31505.6 i/s - same-ish: difference falls within error
ruby: 22081.8 i/s - 1.45x (± 0.00) slower
Cache hit CPU time
ruby : 3.7590459999999997
hiredis: 1.9650980000000011
myredis: 1.7395270000000007
Cache miss (returns nil)
Warming up --------------------------------------
ruby 2.666k i/100ms
hiredis 3.352k i/100ms
myredis 3.440k i/100ms
Calculating -------------------------------------
ruby 25.796k (± 8.2%) i/s - 127.968k in 5.006173s
hiredis 33.267k (± 1.0%) i/s - 167.600k in 5.038516s
myredis 34.693k (± 2.3%) i/s - 175.440k in 5.059694s
Comparison:
myredis: 34693.1 i/s
hiredis: 33267.0 i/s - 1.04x (± 0.00) slower
ruby: 25795.8 i/s - 1.34x (± 0.00) slower
Cache miss CPU time
ruby : 3.0532809999999984
hiredis: 1.840083
myredis: 1.5741099999999975
Fetching gem metadata from https://rubygems.org/....
Resolving dependencies...
Using bundler 2.2.15
Using hiredis 0.6.3
Using redis 4.2.5
Using benchmark-ips 2.8.4
Cache-hit function check passed
Cache-miss function check passed
Cache hit (returns a 2KB string)
Warming up --------------------------------------
ruby 2.638k i/100ms
hiredis 5.080k i/100ms
myredis 5.741k i/100ms
Calculating -------------------------------------
ruby 35.924k (±12.0%) i/s - 176.746k in 5.013467s
hiredis 52.624k (± 4.7%) i/s - 264.160k in 5.034100s
myredis 59.541k (± 1.1%) i/s - 298.532k in 5.014476s
Comparison:
myredis: 59541.4 i/s
hiredis: 52623.8 i/s - 1.13x (± 0.00) slower
ruby: 35924.1 i/s - 1.66x (± 0.00) slower
Cache hit CPU time
ruby : 1.751384999999999
hiredis: 0.9159620000000004
myredis: 0.6444190000000001
Cache miss (returns nil)
Warming up --------------------------------------
ruby 4.234k i/100ms
hiredis 5.594k i/100ms
myredis 6.340k i/100ms
Calculating -------------------------------------
ruby 42.602k (± 9.1%) i/s - 211.700k in 5.020637s
hiredis 55.257k (± 4.7%) i/s - 279.700k in 5.074557s
myredis 63.400k (± 1.6%) i/s - 317.000k in 5.001319s
Comparison:
myredis: 63399.9 i/s
hiredis: 55256.9 i/s - 1.15x (± 0.00) slower
ruby: 42601.6 i/s - 1.49x (± 0.00) slower
Cache miss CPU time
ruby : 1.417432999999999
hiredis: 0.832987000000001
myredis: 0.5722979999999982
@Drowze
Copy link

Drowze commented Feb 6, 2026

Thanks for this script! It seems not compatible with Redis 5 though, so I ran with this change:

-   gem 'redis'
+   gem 'redis', '< 5'

Results on my macbook m3 pro, ruby 3.4.8:

Fetching gem metadata from https://rubygems.org/....
Resolving dependencies...
redis_clients_benchmark.rb:36: warning: benchmark was loaded from the standard library, but will no longer be part of the default gems starting from Ruby 4.0.0.
You can add benchmark to your Gemfile or gemspec to silence this warning.
Cache-hit function check passed
Cache-miss function check passed

Cache hit (returns a 2KB string)
ruby 3.4.8 (2025-12-17 revision 995b59f666) +PRISM [arm64-darwin24]
Warming up --------------------------------------
                ruby   270.000 i/100ms
             hiredis   296.000 i/100ms
             myredis   307.000 i/100ms
Calculating -------------------------------------
                ruby      2.991k (± 8.6%) i/s  (334.34 μs/i) -     14.850k in   5.003609s
             hiredis      3.543k (± 8.6%) i/s  (282.23 μs/i) -     17.760k in   5.045779s
             myredis      3.478k (± 6.0%) i/s  (287.54 μs/i) -     17.499k in   5.050804s

Comparison:
             hiredis:     3543.2 i/s
             myredis:     3477.8 i/s - same-ish: difference falls within error
                ruby:     2991.0 i/s - same-ish: difference falls within error


Cache hit CPU time
ruby   : 6.606366
hiredis: 4.617767
myredis: 3.2234870000000004

Cache miss (returns nil)
ruby 3.4.8 (2025-12-17 revision 995b59f666) +PRISM [arm64-darwin24]
Warming up --------------------------------------
                ruby   345.000 i/100ms
             hiredis   372.000 i/100ms
             myredis   386.000 i/100ms
Calculating -------------------------------------
                ruby      3.517k (±10.3%) i/s  (284.34 μs/i) -     17.595k in   5.051507s
             hiredis      3.609k (± 5.0%) i/s  (277.06 μs/i) -     18.228k in   5.061332s
             myredis      4.053k (±15.3%) i/s  (246.72 μs/i) -     20.072k in   5.043282s

Comparison:
             myredis:     4053.2 i/s
             hiredis:     3609.3 i/s - same-ish: difference falls within error
                ruby:     3517.0 i/s - same-ish: difference falls within error


Cache miss CPU time
ruby   : 7.849893000000002
hiredis: 5.808086000000001
myredis: 4.567702999999998

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment