Skip to content

Commit dab3fd6

Browse files
authored
perf: reduce memory allocations for string and array (#125)
1 parent 94c5319 commit dab3fd6

File tree

5 files changed

+38
-8
lines changed

5 files changed

+38
-8
lines changed

docs/class_diagrams_redis_client.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ classDiagram
1919
+write_timeout=()
2020
+pubsub()
2121
+call()
22+
+call_v()
2223
+call_once()
24+
+call_once_v()
2325
+blocking_call()
26+
+blocking_call_v()
2427
+scan()
2528
+sscan()
2629
+hscan()
@@ -46,19 +49,23 @@ classDiagram
4649
class RedisClient_PubSub {
4750
+initialize()
4851
+call()
52+
+call_v()
4953
+close()
5054
+next_event()
5155
}
5256
5357
class RedisClient_Multi {
5458
+initialize()
5559
+call()
60+
+call_v()
5661
+call_once()
62+
+call_once_v()
5763
}
5864
5965
class RedisClient_Pipeline {
6066
+initialize()
6167
+blocking_call()
68+
+blocking_call_v()
6269
}
6370
6471
class RedisClient_RubyConnection_BufferedIO {

docs/class_diagrams_redis_cluster_client.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,18 @@ classDiagram
9999
100100
class RedisClient_Cluster_Pipeline {
101101
+call()
102+
+call_v()
102103
+call_once()
104+
+call_once_v()
103105
+blocking_call()
106+
+blocking_call_v()
104107
+empty?()
105108
+execute()
106109
}
107110
108111
class RedisClient_Cluster_PubSub {
109112
+call()
113+
+call_v()
110114
+close()
111115
+next_event()
112116
}

lib/redis_client/cluster/command.rb

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
class RedisClient
77
class Cluster
88
class Command
9+
EMPTY_STRING = ''
10+
911
class << self
1012
def load(nodes) # rubocop:disable Metrics/MethodLength
1113
errors = []
@@ -36,11 +38,12 @@ def parse_command_details(rows)
3638

3739
def initialize(details)
3840
@details = pick_details(details)
41+
@normalized_cmd_name_cache = {}
3942
end
4043

4144
def extract_first_key(command)
4245
i = determine_first_key_position(command)
43-
return '' if i == 0
46+
return EMPTY_STRING if i == 0
4447

4548
key = (command[i].is_a?(Array) ? command[i].flatten.first : command[i]).to_s
4649
hash_tag = extract_hash_tag(key)
@@ -72,14 +75,14 @@ def pick_details(details)
7275
end
7376

7477
def dig_details(command, key)
75-
name = command&.flatten&.first.to_s.downcase # OPTIMIZE: prevent allocation for string
78+
name = normalize_cmd_name(command)
7679
return if name.empty? || !@details.key?(name)
7780

7881
@details.fetch(name).fetch(key)
7982
end
8083

8184
def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
82-
case command&.flatten&.first.to_s.downcase # OPTIMIZE: prevent allocation for string
85+
case normalize_cmd_name(command)
8386
when 'eval', 'evalsha', 'zinterstore', 'zunionstore' then 3
8487
when 'object' then 2
8588
when 'memory'
@@ -104,10 +107,23 @@ def extract_hash_tag(key)
104107
s = key.index('{')
105108
e = key.index('}', s.to_i + 1)
106109

107-
return '' if s.nil? || e.nil?
110+
return EMPTY_STRING if s.nil? || e.nil?
108111

109112
key[s + 1..e - 1]
110113
end
114+
115+
def normalize_cmd_name(command)
116+
return EMPTY_STRING unless command.is_a?(Array)
117+
118+
name = case e = command.first
119+
when String then e
120+
when Array then e.first
121+
end
122+
return EMPTY_STRING if name.nil? || name.empty?
123+
124+
@normalized_cmd_name_cache[name] = name.downcase unless @normalized_cmd_name_cache.key?(name)
125+
@normalized_cmd_name_cache[name]
126+
end
111127
end
112128
end
113129
end

lib/redis_client/cluster/pipeline.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,11 @@ def execute # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Met
6666
Thread.new(@router, k, v) do |router, node_key, rows|
6767
Thread.pass
6868
replies = router.find_node(node_key).pipelined do |pipeline|
69-
rows.each do |(_size, *row, block)|
70-
pipeline.send(*row, &block)
69+
rows.each do |row|
70+
case row.size
71+
when 4 then pipeline.send(row[1], row[2], &row[3])
72+
when 5 then pipeline.send(row[1], row[2], row[3], &row[4])
73+
end
7174
end
7275
end
7376

test/prof_mem.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ def run
1818

1919
profile do
2020
send("new_#{cli_type}_client".to_sym).pipelined do |pi|
21-
ATTEMPT_COUNT.times { |i| pi.call('SET', "key#{i}", i) }
22-
ATTEMPT_COUNT.times { |i| pi.call('GET', "key#{i}") }
21+
ATTEMPT_COUNT.times { |i| pi.call('SET', i, i) }
22+
ATTEMPT_COUNT.times { |i| pi.call('GET', i) }
2323
end
2424
end
2525
end

0 commit comments

Comments
 (0)