From 54e000407a63d273a78f837ba35ab67c77454be8 Mon Sep 17 00:00:00 2001 From: Matt Robenolt Date: Sun, 11 Oct 2015 02:57:58 -0700 Subject: [PATCH] Cache Parse() results Each parse takes ~2ms on my machine, and it's pretty common throughout the life of a running process, to parse identical user-agent strings. This adds a very primitive cache similar in vein to the cache inside the `urlparse` package. Before: ``` $ python -m timeit -s 'from ua_parser.user_agent_parser import Parse' 'Parse("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.52 Safari/537.36")' 100 loops, best of 3: 2.14 msec per loop ``` After: ``` $ python -m timeit -s 'from ua_parser.user_agent_parser import Parse' 'Parse("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.52 Safari/537.36")' 1000000 loops, best of 3: 0.956 usec per loop ``` --- Makefile | 8 +++----- ua_parser/user_agent_parser.py | 14 +++++++++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index ae741a8..9ab8a12 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,9 @@ -PWD = $(shell pwd) - -all: prep test +all: prep test prep: #git submodule update --init #sudo apt-get install python-yaml - + test: @#test ! -d tmp && mkdir tmp @export PYTHONPATH=tmp && python setup.py develop -d tmp @@ -21,4 +19,4 @@ clean: @rm -rf tmp\ ua_parser.egg-info -.PHONY: all clean +.PHONY: all prep test clean diff --git a/ua_parser/user_agent_parser.py b/ua_parser/user_agent_parser.py index 613c16e..59cf9fe 100644 --- a/ua_parser/user_agent_parser.py +++ b/ua_parser/user_agent_parser.py @@ -196,6 +196,10 @@ def Parse(self, user_agent_string): return device, brand, model +MAX_CACHE_SIZE = 20 +_parse_cache = {} + + def Parse(user_agent_string, **jsParseBits): """ Parse all the things Args: @@ -205,12 +209,20 @@ def Parse(user_agent_string, **jsParseBits): A dictionary containing all parsed bits """ jsParseBits = jsParseBits or {} - return { + key = (user_agent_string, repr(jsParseBits)) + cached = _parse_cache.get(key) + if cached is not None: + return cached + if len(_parse_cache) > MAX_CACHE_SIZE: + _parse_cache.clear() + v = { 'user_agent': ParseUserAgent(user_agent_string, **jsParseBits), 'os': ParseOS(user_agent_string, **jsParseBits), 'device': ParseDevice(user_agent_string, **jsParseBits), 'string': user_agent_string } + _parse_cache[key] = v + return v def ParseUserAgent(user_agent_string, **jsParseBits):