Skip to content

Commit 6ec8e61

Browse files
authored
optimize: make host match ~15% faster (#59)
1 parent 5b663e9 commit 6ec8e61

File tree

5 files changed

+155
-25
lines changed

5 files changed

+155
-25
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ bench:
8585
@echo ""
8686
resty -I=./lib -I=./deps/share/lua/5.1 benchmark/match-static.lua
8787
@echo ""
88+
resty -I=./lib -I=./deps/share/lua/5.1 benchmark/match-hosts.lua
89+
@echo ""
90+
resty -I=./lib -I=./deps/share/lua/5.1 benchmark/match-wildcard-hosts.lua
91+
@echo ""
8892

8993

9094
### help: Show Makefile rules

benchmark/match-hosts.lua

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
local radix = require("resty.radixtree")
2+
local route_count = 1000 * 1
3+
local match_times = 1000 * 5
4+
5+
local path = "/12345"
6+
local routes = {}
7+
for i = 1, route_count do
8+
routes[i] = {paths = {path}, hosts = {ngx.md5(i)}, metadata = i}
9+
end
10+
11+
local rx = radix.new(routes)
12+
13+
ngx.update_time()
14+
local start_time = ngx.now()
15+
16+
local res
17+
local opts = {
18+
host = ngx.md5(500),
19+
}
20+
for _ = 1, match_times do
21+
res = rx:match(path, opts)
22+
end
23+
24+
ngx.update_time()
25+
local used_time = ngx.now() - start_time
26+
ngx.say("matched res: ", res)
27+
ngx.say("route count: ", route_count)
28+
ngx.say("match times: ", match_times)
29+
ngx.say("time used : ", used_time, " sec")
30+
ngx.say("QPS : ", math.floor(match_times / used_time))

benchmark/match-wildcard-hosts.lua

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
local radix = require("resty.radixtree")
2+
local route_count = 1000 * 1
3+
local match_times = 1000 * 5
4+
5+
local path = "/12345"
6+
local routes = {}
7+
for i = 1, route_count do
8+
routes[i] = {paths = {path}, hosts = {"*." .. ngx.md5(i)}, metadata = i}
9+
end
10+
11+
local rx = radix.new(routes)
12+
13+
ngx.update_time()
14+
local start_time = ngx.now()
15+
16+
local res
17+
local opts = {
18+
host = "1." .. ngx.md5(500),
19+
}
20+
for _ = 1, match_times do
21+
res = rx:match(path, opts)
22+
end
23+
24+
ngx.update_time()
25+
local used_time = ngx.now() - start_time
26+
ngx.say("matched res: ", res)
27+
ngx.say("route count: ", route_count)
28+
ngx.say("match times: ", match_times)
29+
ngx.say("time used : ", used_time, " sec")
30+
ngx.say("QPS : ", math.floor(match_times / used_time))

lib/resty/radixtree.lua

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ local new_tab = base.new_tab
3030
local tonumber = tonumber
3131
local ipairs = ipairs
3232
local ffi = require("ffi")
33+
local C = ffi.C
3334
local ffi_cast = ffi.cast
3435
local ffi_cdef = ffi.cdef
3536
local insert_tab = table.insert
@@ -101,6 +102,8 @@ end
101102

102103

103104
ffi_cdef[[
105+
int memcmp(const void *s1, const void *s2, size_t n);
106+
104107
void *radix_tree_new();
105108
int radix_tree_destroy(void *t);
106109
int radix_tree_insert(void *t, const unsigned char *buf, size_t len,
@@ -126,6 +129,18 @@ end
126129
local _M = { _VERSION = 1.7 }
127130

128131

132+
local function has_suffix(s, suffix)
133+
if type(s) ~= "string" or type(suffix) ~= "string" then
134+
return false
135+
end
136+
if #s < #suffix then
137+
return false
138+
end
139+
local rc = C.memcmp(ffi.cast("char *", s) + #s - #suffix, suffix, #suffix)
140+
return rc == 0
141+
end
142+
143+
129144
-- only work under lua51 or luajit
130145
local function setmt__gc(t, mt)
131146
local prox = newproxy(true)
@@ -258,9 +273,7 @@ function pre_insert_route(self, path, route)
258273
local is_wildcard = false
259274
if h and h:sub(1, 1) == '*' then
260275
is_wildcard = true
261-
h = h:sub(2):reverse()
262-
else
263-
h = h:reverse()
276+
h = h:sub(2)
264277
end
265278

266279
insert_tab(route_opts.hosts, is_wildcard)
@@ -272,9 +285,7 @@ function pre_insert_route(self, path, route)
272285
local host = hosts
273286
if host:sub(1, 1) == '*' then
274287
is_wildcard = true
275-
host = host:sub(2):reverse()
276-
else
277-
host = host:reverse()
288+
host = host:sub(2)
278289
end
279290

280291
route_opts.hosts = {is_wildcard, host}
@@ -383,20 +394,11 @@ end
383394

384395

385396
local function match_host(route_host_is_wildcard, route_host, request_host)
386-
if type(request_host) ~= "string" or #route_host > #request_host then
387-
return false
388-
end
389-
390397
if not route_host_is_wildcard then
391398
return route_host == request_host
392399
end
393400

394-
local i = request_host:find(route_host, 1, true)
395-
if i ~= 1 then
396-
return false
397-
end
398-
399-
return true
401+
return has_suffix(request_host, route_host)
400402
end
401403

402404

@@ -564,20 +566,16 @@ local function match_route_opts(route, opts, ...)
564566
if route.hosts then
565567
local matched = false
566568

567-
if opts.host and not opts.host_reversed then
568-
opts.host_reversed = opts.host:reverse()
569-
end
570-
571569
local hosts = route.hosts
572-
local reverse_host = opts.host_reversed
573-
if reverse_host then
570+
local host = opts.host
571+
if host then
574572
for i = 1, #hosts, 2 do
575-
if match_host(hosts[i], hosts[i + 1], reverse_host) then
573+
if match_host(hosts[i], hosts[i + 1], host) then
576574
if opts_matched_exists then
577575
if hosts[i] then
578-
opts.matched._host = (hosts[i + 1] .. "*"):reverse()
576+
opts.matched._host = "*" .. hosts[i + 1]
579577
else
580-
opts.matched._host = hosts[i + 1]:reverse()
578+
opts.matched._host = opts.host
581579
end
582580
end
583581
matched = true

t/host.t

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ __DATA__
2222
2323
ngx.say(rx:match("/aa/bb", {host = "foo.com"}))
2424
ngx.say(rx:match("/aa/bb", {host = "www.foo.com"}))
25+
26+
local opts = {host = "foo.com", matched = {}}
27+
rx:match("/aa/bb", opts)
28+
ngx.say("matched: ", opts.matched._host)
2529
}
2630
}
2731
--- request
@@ -31,6 +35,7 @@ GET /t
3135
--- response_body
3236
metadata /aa
3337
nil
38+
matched: foo.com
3439

3540

3641

@@ -51,6 +56,10 @@ nil
5156
ngx.say(rx:match("/aa/bb", {host = ".foo.com"}))
5257
ngx.say(rx:match("/aa/bb", {host = "www.foo.com"}))
5358
ngx.say(rx:match("/aa/bb", {host = "www.bar.foo.com"}))
59+
60+
local opts = {host = "www.foo.com", matched = {}}
61+
rx:match("/aa/bb", opts)
62+
ngx.say("matched: ", opts.matched._host)
5463
}
5564
}
5665
--- request
@@ -62,6 +71,7 @@ nil
6271
metadata /aa
6372
metadata /aa
6473
metadata /aa
74+
matched: *.foo.com
6575

6676

6777

@@ -122,3 +132,61 @@ metadata /aa
122132
nil
123133
metadata /aa
124134
nil
135+
136+
137+
138+
=== TEST 5: hosts in string type
139+
--- config
140+
location /t {
141+
content_by_lua_block {
142+
local radix = require("resty.radixtree")
143+
local rx = radix.new({
144+
{
145+
paths = {"/aa*"},
146+
metadata = "metadata /aa",
147+
hosts = "foo.com",
148+
}
149+
})
150+
151+
ngx.say(rx:match("/aa/bb", {host = "foo.com"}))
152+
ngx.say(rx:match("/aa/bb", {host = "www.foo.com"}))
153+
}
154+
}
155+
--- request
156+
GET /t
157+
--- no_error_log
158+
[error]
159+
--- response_body
160+
metadata /aa
161+
nil
162+
163+
164+
165+
=== TEST 6: wildcard hosts in string type
166+
--- config
167+
location /t {
168+
content_by_lua_block {
169+
local radix = require("resty.radixtree")
170+
local rx = radix.new({
171+
{
172+
paths = {"/aa*"},
173+
hosts = "*.foo.com",
174+
metadata = "metadata /aa",
175+
}
176+
})
177+
178+
ngx.say(rx:match("/aa/bb", {host = "foo.com"}))
179+
ngx.say(rx:match("/aa/bb", {host = ".foo.com"}))
180+
ngx.say(rx:match("/aa/bb", {host = "www.foo.com"}))
181+
ngx.say(rx:match("/aa/bb", {host = "www.bar.foo.com"}))
182+
}
183+
}
184+
--- request
185+
GET /t
186+
--- no_error_log
187+
[error]
188+
--- response_body
189+
nil
190+
metadata /aa
191+
metadata /aa
192+
metadata /aa

0 commit comments

Comments
 (0)