optimization: faster mask matching

Massimo Melina committed Feb 16, 2025 at 14:23 UTC d47f0873a454e15ebf5f18fef78029fc2074d481
3 files changed +7 -3
src/cross.ts
+3 -1
@@ -465,8 +465,10 @@ export function makeMatcher(mask: string, emptyMaskReturns=false) {
465 : () => emptyMaskReturns
466 }
467
468 +// this is caching all matchers, so don't use it with frequently changing masks. Benchmarks revealed that _.memoize make it slower than not using it, while this simple cache can speed up to 30x
469 export function matches(s: string, mask: string, emptyMaskReturns=false) {
469 - return makeMatcher(mask, emptyMaskReturns)(s)
470 + const cache = (matches as any).cache ||= {}
471 + return (cache[mask + (emptyMaskReturns ? '1' : '0')] ||= makeMatcher(mask, emptyMaskReturns))(s)
472 }
473
474 // if delimiter is specified, it is prefixed to symbols. If it contains a space, the part after the space is considered as suffix.
src/geo.ts
+1
@@ -14,6 +14,7 @@ const allowUnknown = defineConfig(CFG.geo_allow_unknown, false)
14 enabled.sub(checkFiles)
15 setInterval(checkFiles, DAY) // keep updated at run-time
16
17 +// benchmark: memoize can make this 44x faster
18 export const ip2country = _.memoize((ip: string) => ip2location.getCountryShortAsync(ip).then(v => v === '-' ? '' : v, () => ''))
19
20 export const geoFilter: Middleware = async (ctx, next) => {
src/misc.ts
+3 -2
@@ -33,9 +33,10 @@ export function isLocalHost(c: Connection | Koa.Context | string) {
33 return ip && isIpLocalHost(ip)
34 }
35
36 -// this will memory-leak over mask, so be careful with what you use this
36 +// this will memory-leak over mask, so be careful with what you use this. Object is 3x faster than _.memoize
37 export function netMatches(ip: string, mask: string, emptyMaskReturns=false) {
38 - return _.memoize(makeNetMatcher, (a,b) => `${a}\t${b ? 1 : 0}`)(mask, emptyMaskReturns)(ip) // cache the matcher
38 + const cache = (netMatches as any).cache ||= {}
39 + return (cache[mask + (emptyMaskReturns ? '1' : '0')] ||= makeNetMatcher(mask, emptyMaskReturns))(ip) // cache the matcher
40 }
41 export function makeNetMatcher(mask: string, emptyMaskReturns=false) {
42 if (!mask)