SlideShare a Scribd company logo
1 of 66
Download to read offline
How To Make The
Fastest Router
In Python
Makoto Kuwata
https://github.com/kwatch/
PloneConference 2018 Tokyo
Abstract
‣ TL;DR
‣ You can make router much faster (max: x10)
‣ Requirements
‣ Python3
‣ Experience of Web Application Framework (Django, Flask, Plone, etc)
‣ Sample Code
‣ https://github.com/kwatch/router-sample/
‣ https://github.com/kwatch/keight/tree/python/ (Framework)
Table of Contents
‣ What is Router?
‣ Linear Search
‣ Naive / Prefix String / Fixed Path Dictionary
‣ Regular Expression
‣ Naive / Smart / Optimized
‣ State Machine
‣ Conclusion
What is Router?
What is Router?
‣ Router is a component of web app framework (WAF).
‣ Router determines request handler according to request
method and request path.

Handler A
App
Server
Handler B
Handler C
Client
: HTTP Request
: HTTP Response
WSGI
App
Server Side
Router determines
"which handler?"
Request Handler Example
class BooksAPI(RequestHandler):
with on.path('/api/books/'):
@on('GET')
def do_index(self):
return {"action": "index"}
@on('POST')
def do_create(self):
return {"action": "create"}
....
← handler class
← URL Path
← request method
← handler func
← request method
← handler func
Request Handler Example
....
with on.path('/api/books/{id:int}'):
@on('GET')
def do_show(self, id):
return {"action": "show", "id": id}
@on('PUT')
def do_update(self, id):
return {"action": "update", "id": id}
@on('DELETE')
def do_delete(self, id):
return {"action": "delete", "id": id}
← URL Path
← request method
← handler func
← request method
← handler func
← request method
← handler func
Mapping Example: Request to Handler
mapping_table = [
## method path class func
("GET" , r"/api/books/" , BooksAPI , do_index),
("POST" , r"/api/books/" , BooksAPI , do_create),

("GET" , r"/api/books/(d+)" , BooksAPI , do_show),
("PUT" , r"/api/books/(d+)" , BooksAPI , do_update),

("DELETE", r"/api/books/(d+)" , BooksAPI , do_delete),

("GET" , r"/api/orders/" , OrdersAPI, do_index),

("POST" , r"/api/orders/" , OrdersAPI, do_create),

("GET" , r"/api/orders/(d+)", OrdersAPI, do_show),
("PUT" , r"/api/orders/(d+)", OrdersAPI, do_update),

("DELETE", r"/api/orders/(d+)", OrdersAPI, do_delete),

....
]
Mapping Example: Request to Handler
mapping_list = [
## path class {method: func}
(r"/api/books/" , BooksAPI , {"GET": do_index,
"POST": do_create}),
(r"/api/books/(d+)" , BooksAPI , {"GET": do_show,
"PUT": do_update,
"DELETE": do_delete}),

(r"/api/orders/" , OrdersAPI, {"GET": do_index,
"POST": do_create}),
(r"/api/orders/(d+)", OrdersAPI, {"GET": do_show,
"PUT": do_update,
"DELETE": do_delete}),

....
]
Same information
in different format
Router Example
>>> router = Router(mapping_list)
>>> router.lookup("GET", "/api/books/")
(BooksAPI, do_index, [])
>>> router.lookup("GET", "/api/books/123")
(BooksAPI, do_show, [123])
Router Example
### 404 Not Found
>>> router.lookup("GET", "/api/books/123/comments")
(None, None, None)
### 405 Method Not Allowed
>>> router.lookup("POST", "/api/books/123")
(BooksAPI, None, [123])
Linear Search
(Naive)
Linear Search
mapping = [
(r"/api/books/" , BooksAPI , {"GET": do_index,
"POST": do_create}),
(r"/api/books/(d+)" , BooksAPI , {"GET": do_show,
"PUT": do_update,
"DELETE": do_delete}),

(r"/api/orders/" , OrdersAPI, {"GET": do_index,
"POST": do_create}),
(r"/api/orders/(d+)", OrdersAPI, {"GET": do_show,
"PUT": do_update,
"DELETE": do_delete}),

....
]
Router Class
class LinearNaiveRouter(Router):
def __init__(self, mapping):
self._mapping_list = 
[ (compile_path(path), klass, funcs)
for path, klass, funcs in mapping ]
def lookup(req_meth, req_path):
for rexp, klass, funcs in self._mapping_list:
m = rexp.match(req_path)
if m:
params = [ int(v) for v in m.groups() ]
func = funcs.get(req_meth)
return klass, func, params
return None, None, None
Benchmark (Data)
mapping_list = [
(r'/api/aaa' , DummyAPI, {"GET": ...}),
(r'/api/aaa/{id:int}', DummyAPI, {"GET": ...}),
(r'/api/bbb' , DummyAPI, {"GET": ...}),
(r'/api/bbb/{id:int}', DummyAPI, {"GET": ...}),
....
(r'/api/yyy' , DummyAPI, {"GET": ...}),
(r'/api/yyy/{id:int}', DummyAPI, {"GET": ...}),
(r'/api/zzz' , DummyAPI, {"GET": ...}),
(r'/api/zzz/{id:int}', DummyAPI, {"GET": ...}),
]
### Benchmark environment:
### AWS EC2 t3.nano, Ubuntu 18.04, Python 3.6.6
See sample code
for details
Benchmark
0 10 20 30 40 50
Linear Naive
Seconds
(1M Requests)
/api/aaa
/api/aaa/{id}
/api/zzz
/api/zzz/{id}
sec
SlowerFaster
very fast on top of list
(/api/aaa, /api/aaa/{id})
very slow on bottom of list
(/api/zzz, /api/zzz/{id})
Pros.
Cons.
Pros & Cons
✗ Very slow when many mapping entries exist.
✓ Easy to understand and implement
Linear Search
(Prefix String)
Prefix String
mapping_list = [
("/books" , r"/books" , BooksAPI , {"GET": ...}),

("/books/" , r"/books/(d+)" , BooksAPI , {"GET": ...}),

("/orders" , r"/orders" , OrdersAPI, {"GET": ...}),

("/orders/", r"/orders/(d+)", OrdersAPI, {"GET": ...}),

]
for prefix, rexp, klass, funcs in mapping:
if not "/api/orders/123".startswith(prefix):
continue
m = rexp.match("/api/orders/123")
if m:
...
Much faster than
rexp.match()
(replace expensive operation
with cheap operation)
Prefix strings
Router Class
def prefix_str(s):
return s.split('{', 1)[0]
class PrefixLinearRouter(Router):
def __init__(self, mapping):
for path, klass, funcs in mapping:
prefix = prefix_str(path)
rexp = compile_path(path)
t = (prefix, rexp, klass, funcs)
self._mapping_list.append(t)
...
Router Class
....
def lookup(req_meth, req_path):
for prefix, rexp, klass, funcs in self._mapping:
if not req_path.startswith(prefix):
continue
m = rexp.match(req_path)
if m:
params = [ int(v) for v in m.groups() ]
func = funcs.get(req_meth)
return klass, func, params
return None, None, None
Much faster than
rexp.match()
Benchmark
0 10 20 30 40 50
Linear Naive
Prefix Str
Seconds
(1M Requests)
/api/aaa
/api/aaa/{id}
/api/zzz
/api/zzz/{id}
sec
SlowerFaster
about twice as fast as
naive implementation
Pros.
Cons.
Pros & Cons
✗ Still slow when many mapping entries exist.
✓ Makes linear search faster.
✓ Easy to understand and implement.
Linear Search
(Fixed Path Dictionary)
Fixed Path Dictionary
## variable path (contains one or more path parameters)
mapping_list = [
("/books" , r"/books" , BooksAPI , {"GET": ...}),

("/books/" , r"/books/(d+)" , BooksAPI , {"GET": ...}),

("/orders" , r"/orders" , OrdersAPI, {"GET": ...}),

("/orders/", r"/orders/(d+)", OrdersAPI, {"GET": ...}),

]
## fixed path (contains no path parameters)
mapping_dict = {
r"/books" : (BooksAPI , {"GET": ...}, []),
r"/orders": (OrdersAPI, {"GET": ...}, []),
}
Use fixed path as key of dict
Move fixed path to dict
Router Class
class FixedLinearRouter(object):
def __init__(self, mapping):
self._mapping_dict = {}
self._mapping_list = []
for path, klass, funcs in mapping:
if '{' not in path:
self._mapping_dict[path] = (klass, funcs, [])

else:
prefix = prefix_str(path)
rexp = compile_path(path)
t = (prefix, rexp, klass, funcs)
self._mapping_list.append(t)
....
Router Class
....
def lookup(req_meth, req_path):
t = self._mapping_dict.get(req_path)
if t: return t
for prefix, rexp, klass, funcs in self._mapping_list:

if not req_path.startswith(prefix)
continue
m = rexp.match(req_path)
if m:
params = [ int(v) for v in m.groups() ]
func = funcs.get(req_meth)
return klass, func, params
return None, None, None
Much faster than
for-loop
Number of entries
are reduced
Benchmark
0 10 20 30 40 50
Linear Naive
Prefix Str
Fixed Path
Seconds
(1M Requests)
/api/aaa
/api/aaa/{id}
/api/zzz
/api/zzz/{id}
sec
SlowerFaster
super fast on fixed path!
three times faster than
naive implementation
Pros.
Cons.
Pros & Cons
✗ Still slow when many mapping entries exist.
✓ Makes fixed path search super faster.
✓ Makes variable path search faster,

because number of entries are reduced.
✓ Easy to understand and implement.
Notice
‣ Don't use r"/api/v{version:int}".
‣ because all API paths are regarded as variable path.
‣ Instead, use r"/api/v1", r"/api/v2", ...
‣ in order to increase number of fixed path.
Regular Expression
(Naive)
Concatenate Regular Expressions
mapping_list = {
(r"/api/books/(d+)" , BooksAPI , {"GET": ...}),

(r"/api/orders/(d+)", OrdersAPI, {"GET": ...}),

(r"/api/users/(d+)" , UsersAPI , {"GET": ...}),

]
arr = [
r"(?P<_0>^/api/books/(d+)$)",
r"(?P<_1>^/api/orders/(d+)$)",
r"(?P<_2>^/api/users/(d+)$)",
]
all_rexp = re.compile("|".join(arr))
Named groups
Matching
m = all_rexp.match("/api/users/123")
d = m.groupdict() #=> {"_0": None,
# "_1": None,
# "_2": "/api/users/123"}
for k, v in d.items():
if v:
i = int(v[1:]) # ex: "_2" -> 2
break
_, klass, funcs, pos, nparams = mapping_list[i]
arr = m.groups() #=> (None, None, None, None,
# "/api/users/123", "123")
params = arr[5:6] #=> {"123"}
Router Class
class NaiveRegexpRouter(Router):
def __init__(self, mapping):
self._mapping_dict = {}
self._mapping_list = []
arr = []; i = 0; pos = 0
for path, klass, funcs in mapping:
if '{' not in path:
self._mapping_dict[path] = (klass, funcs, [])
else:
rexp = compile_path(path); pat = rexp.pattern
arr.append("(?P<_%s>%s)" % (i, pat))
t = (klass, funcs, pos, path.count('{'))
self._mapping_list.append(t)
i += 1; pos += 1 + path.count('{')
self._all_rexp = re.compile("|".join(arr))
Router Class
....
def lookup(req_meth, req_path):
t = self._mapping_dict.get(req_path)
if t: return t
m = self._all_rexp.match(req_path)
if m:
for k, v in m.groupdict().items():
if v:
i = int(v[1:])
break
klass, funcs, pos, nparams = self._mapping_list[i]

params = m.groups()[pos:pos+nparams]
func = funcs.get(req_meth)
return klass, func, params
return None, None, None
find index in list
find param values
Benchmark
0 10 20 30 40 50
Linear
Regexp
Naive
Prefix Str
Fixed Path
Naive
Seconds
(1M Requests)
/api/aaa
/api/aaa/{id}
/api/zzz
/api/zzz/{id}
sec
SlowerFaster
slower than
linear search :(
Pros.
Cons.
Pros & Cons
✗ Slower than linear search
✓ Nothing :(
Notice
$ python3 --version
3.4.5
$ python3
>>> import re
>>> arr = ['^/(d+)$'] * 101
>>> re.compile("|".join(arr))
File "/opt/vs/python/3.4.5/lib/python3.4/sre_compile.py",
line 579, in compile
"sorry, but this version only supports 100 named groups"
AssertionError: sorry, but this version only supports 100
named groups
Python <= 3.4 limits number of
groups in a regular expression,
and no work around :(
Regular Expression
(Smart)
Improved Regular Expression
mapping_list = {
(r"/api/books/(d+)" , BooksAPI , {"GET": ...}),
(r"/api/orders/(d+)" , OrdersAPI , {"GET": ...}),
(r"/api/users/(d+)" , UsersAPI , {"GET": ...}),
]
arr = [ r"^/api/books/(?:d+)($)",
r"^/api/orders/(?:d+)($)",
r"^/api/users/(?:d+)($)", ]
all_rexp = re.compile("|".join(arr))
m = all_rexp.match("/api/users/123")
arr = m.groups() #=> (None, None, "")
i = arr.index("") #=> 2
t = mapping_list[i] #=> (r"/api/users/(d+)",
# UsersAPI, {"GET": ...})
No more
named groups
Tuple is much light-
weight than dict
index() is faster
than for-loop
Router Class
class SmartRegexpRouter(Router):
def __init__(self, mapping):
self._mapping_dict = {}
self._mapping_list = []
arr = []
for path, klass, funcs in mapping:
if '{' not in path:
self._mapping_dict[path] = (klass, funcs, [])

else:
rexp = compile_path(path); pat = rexp.pattern

arr.append(pat.replace("(", "(?:")
.replace("$", "($)"))
t = (rexp, klass, funcs)
self._mapping_list.append(t)

self._all_rexp = re.compile("|".join(arr))
Router Class
...
def lookup(req_meth, req_path):
t = self._mapping_dict.get(req_path)
if t: return t
m = self._all_rexp.match(req_path)
if m:
i = m.groups().index("")
rexp, klass, funcs = self._mapping_list[i]
m2 = rexp.match(req_path)
params = [ int(v) for v in m2.groups() ]
func = funcs.get(req_meth)
return klass, func, params
return None, None, None
Matching to find
index in list
Matching to get
param values
Benchmark
0 10 20 30 40 50
Linear
Regexp
Naive
Prefix Str
Fixed Path
Naive
Smart
Seconds
(1M Requests)
/api/aaa
/api/aaa/{id}
/api/zzz
/api/zzz/{id}
sec
SlowerFaster
Difference between
/api/aaa/{id} and
/api/zzz/{id} is small
Pros.
Cons.
Pros & Cons
✗ Slower when number of entries is small.

(due to overhead of twice matching)
✗ May be difficult to debug large regular
expression.
✓ Much faster than ever,

especially when many mapping entries exist.
Regular Expression
(Optimized)
Optimize Regular Expression
## before
arr = [r"^/api/books/(?:d+)($)",
r"^/api/orders/(?:d+)($)",
r"^/api/users/(?:d+)($)"]
all_rexp = re.compile("|".join(arr))
### after
arr = [r"^/api",
r"(?:",
"|".join([r"/books/(?:d+)($)",
r"/orders/(?:d+)($)",
r"/users/(?:d+)($)"]),
r")?"]
all_rexp = re.compile("|".join(arr))
Router Class
class OptimizedRegexpRouter(Router):
def __init__(self, mapping):
## Code is too complicated to show here.
## Please download sample code from github.
## https://github.com/kwatch/router-sample/
def lookup(req_meth, req_path):
## nothing changed; same as previous section
Benchmark
0 10 20 30 40 50
Linear
Regexp
Naive
Prefix Str
Fixed Path
Naive
Smart
Optimized
Seconds
(1M Requests)
/api/aaa
/api/aaa/{id}
/api/zzz
/api/zzz/{id}
sec
SlowerFaster
A little faster
on /api/zzz/{id}
Pros.
Cons.
Pros & Cons
✗ Performance benefit is very small (on Python).
✗ Rather difficult to implement and debug.
✓ A little faster than smart regular expression

when a lot of variable paths exist.
State Machine
State Machine
"api"
"books"
"orders"
"123"
"456"
/d+/
/d+/
/api/books/{id:int}/api/books
/api/orders /api/orders/{id:int}
: Start
: Not Accepted
: Accepted
State Machine: Definition
path = "/api/books"
transition = {
"api": {
"books": {
None: (BooksAPI, {"GET": do_index, ...}),
},
},
}
>>> transition["api"]["books"][None]
(BooksAPI, {"GET": do_index, ...})
>>> transition["api"]["users"][None]
KeyError: 'users'
Use None as terminator
(mark of accepted status)
State Machine: Definition
path = "/api/books/{id:int}"
transition = {
"api": {
"books": {
None: (BooksAPI, {"GET": do_index, ...}),
1: {
None: (BooksAPI, {"GET": do_show, ...}),
},
},
},
}
>>> transition["api"]["books"][1][None]
(BooksAPI, {"GET": do_index, ...})
1 represents int parameter,
2 represents str parameter.
State Machine: Transition
def find(req_path):
req_path = req_path.lstrip('/') #ex: "/a/b/c" -> "a/b/c"
items = req_path.split('/') #ex: "a/b/c" -> ["a","b","c"]
d = transition; params = []
for s in items:
if s in d: d = d[s]
elif 1 in d: d = d[1]; params.append(int(s))
elif 2 in d: d = d[2]; params.append(str(s))
else: return None
if None not in d: return None
klass, funcs = d[None]
return klass, funcs, params
>>> find("/api/books/123")
(BooksAPI, {"GET": do_index, ...}, [123])
Router Class
class StateMachineRouter(Router):
def __init__(self, mapping):
self._mapping_dict = {}
self._mapping_list = []
self._transition = {}
for path, klass, funcs in mapping:
if '{' not in path:
self._mapping_dict[path] = (klass, funcs, [])

else:
self._register(path, klass, funcs)
Router Class
...
PARAM_TYPES = {"int": 1, "str": 2}
def _register(self, path, klass, funcs):
ptypes = self.PARAM_TYPES
d = self._transition
for s in path[1:].split('/'):
key = s
if s[0] == "{" and s[-1] == "}":
## ex: "{id:int}" -> ("id", "int")
pname, ptype = s[1:-1].split(':', 1)
key = ptypes.get(ptype) or ptypes["str"]
d = d.setdefault(key, {})
d[None] = (klass, funcs)
Router Class
...
def lookup(self, req_meth, req_path):
d = self._transition
params = []
for s in req_path[1:].split('/'):
if s in d: d = d[s]
elif 1 in d: d = d[1]; params.append(int(s))
elif 2 in d: d = d[2]; params.append(str(s))
else: return None, None, None
if None in d:
klass, funcs = d[None]
func = funcs.get(req_meth)
return klass, func, params
return None, None, None
Benchmark
0 10 20 30 40 50
Linear
Regexp
StateMachine
Naive
Prefix Str
Fixed Path
Naive
Smart
Optimized
Seconds
(1M Requests)
/api/aaa
/api/aaa/{id}
/api/zzz
/api/zzz/{id}
sec
SlowerFaster
/api/aaa/{id} and
/api/zzz/{id} are
same performance
Benchmark (PyPy3.5)
0 10 20 30 40 50
Linear
Regexp
StateMachine
Naive
Prefix Str
Fixed Path
Naive
Smart
Optimized
Seconds
(1M Requests)
/api/aaa
/api/aaa/{id}
/api/zzz
/api/zzz/{id}
sec
SlowerFaster
Regular Expression is
very slow in PyPy3.5
String operation is
very fast because
JIT friendly
Benchmark (PyPy3.5)
0 1 2 3 4 5
Linear
Regexp
StateMachine
Naive
Prefix Str
Fixed Path
Naive
Smart
Optimized
Seconds
(1M Requests)
/api/aaa
/api/aaa/{id}
/api/zzz
/api/zzz/{id}
sec
SlowerFaster
The fastest method due to
Regexp-free (= JIT friendly)
A little slower than StateMachine
because containing Regexp
Pros.
Cons.
Pros & Cons
✗ Not support complicated pattern.
✗ Requires some effort to support URL path suffix
(ex: /api/books/123.json).
✓ Performance champion in routing area.
✓ Much faster in PyPy3.5, due to regexp-free.
JIT friendly!
Conclusion
Conclusion
‣ Linear Search is slow.
‣ Prefix string and Fixed path dict make it faster.
‣ Regular expression is very fast.
‣ Do your best to avoid named group (or named caption).
‣ State Machine is the fastest method in Python.
‣ Especially in PyPy3, due to regexp-free (= JIT friendly).
One More Thing
My Products
‣ Benchmarker.py
Awesome benchmarking utility.
https://pythonhosted.org/Benchmarker/
‣ Oktest.py
New generation of testing framework.
https://pythonhosted.org/Oktest/
‣ PyTenjin
Super fast and feature-rich template engine.
https://www.kuwata-lab.com/tenjin/pytenjin-users-guide.html
https://bit.ly/tenjinpy_slide (presentation)
Thank You

More Related Content

What's hot

Optimizing Apache Spark Throughput Using Intel Optane and Intel Memory Drive...
 Optimizing Apache Spark Throughput Using Intel Optane and Intel Memory Drive... Optimizing Apache Spark Throughput Using Intel Optane and Intel Memory Drive...
Optimizing Apache Spark Throughput Using Intel Optane and Intel Memory Drive...Databricks
 
Deep Dive into Spark SQL with Advanced Performance Tuning with Xiao Li & Wenc...
Deep Dive into Spark SQL with Advanced Performance Tuning with Xiao Li & Wenc...Deep Dive into Spark SQL with Advanced Performance Tuning with Xiao Li & Wenc...
Deep Dive into Spark SQL with Advanced Performance Tuning with Xiao Li & Wenc...Databricks
 
Using Apache Arrow, Calcite, and Parquet to Build a Relational Cache
Using Apache Arrow, Calcite, and Parquet to Build a Relational CacheUsing Apache Arrow, Calcite, and Parquet to Build a Relational Cache
Using Apache Arrow, Calcite, and Parquet to Build a Relational CacheDremio Corporation
 
DataStax: Extreme Cassandra Optimization: The Sequel
DataStax: Extreme Cassandra Optimization: The SequelDataStax: Extreme Cassandra Optimization: The Sequel
DataStax: Extreme Cassandra Optimization: The SequelDataStax Academy
 
Oracle SQL, PL/SQL Performance tuning
Oracle SQL, PL/SQL Performance tuningOracle SQL, PL/SQL Performance tuning
Oracle SQL, PL/SQL Performance tuningSmitha Padmanabhan
 
SQL for Data Science Tutorial | Data Science Tutorial | Edureka
SQL for Data Science Tutorial | Data Science Tutorial | EdurekaSQL for Data Science Tutorial | Data Science Tutorial | Edureka
SQL for Data Science Tutorial | Data Science Tutorial | EdurekaEdureka!
 
Spark Summit East 2017: Apache spark and object stores
Spark Summit East 2017: Apache spark and object storesSpark Summit East 2017: Apache spark and object stores
Spark Summit East 2017: Apache spark and object storesSteve Loughran
 
Ibm과 nvidia가 제안하는 딥러닝 플랫폼
Ibm과 nvidia가 제안하는 딥러닝 플랫폼Ibm과 nvidia가 제안하는 딥러닝 플랫폼
Ibm과 nvidia가 제안하는 딥러닝 플랫폼ibmrep
 
Apache Spark 2.0: A Deep Dive Into Structured Streaming - by Tathagata Das
Apache Spark 2.0: A Deep Dive Into Structured Streaming - by Tathagata Das Apache Spark 2.0: A Deep Dive Into Structured Streaming - by Tathagata Das
Apache Spark 2.0: A Deep Dive Into Structured Streaming - by Tathagata Das Databricks
 
Facebook Presto presentation
Facebook Presto presentationFacebook Presto presentation
Facebook Presto presentationCyanny LIANG
 
Monitor Apache Spark 3 on Kubernetes using Metrics and Plugins
Monitor Apache Spark 3 on Kubernetes using Metrics and PluginsMonitor Apache Spark 3 on Kubernetes using Metrics and Plugins
Monitor Apache Spark 3 on Kubernetes using Metrics and PluginsDatabricks
 
C* Summit 2013: The World's Next Top Data Model by Patrick McFadin
C* Summit 2013: The World's Next Top Data Model by Patrick McFadinC* Summit 2013: The World's Next Top Data Model by Patrick McFadin
C* Summit 2013: The World's Next Top Data Model by Patrick McFadinDataStax Academy
 
Optimizing Delta/Parquet Data Lakes for Apache Spark
Optimizing Delta/Parquet Data Lakes for Apache SparkOptimizing Delta/Parquet Data Lakes for Apache Spark
Optimizing Delta/Parquet Data Lakes for Apache SparkDatabricks
 
2021 04-20 apache arrow and its impact on the database industry.pptx
2021 04-20  apache arrow and its impact on the database industry.pptx2021 04-20  apache arrow and its impact on the database industry.pptx
2021 04-20 apache arrow and its impact on the database industry.pptxAndrew Lamb
 
How to Analyze and Tune MySQL Queries for Better Performance
How to Analyze and Tune MySQL Queries for Better PerformanceHow to Analyze and Tune MySQL Queries for Better Performance
How to Analyze and Tune MySQL Queries for Better Performanceoysteing
 
Making Structured Streaming Ready for Production
Making Structured Streaming Ready for ProductionMaking Structured Streaming Ready for Production
Making Structured Streaming Ready for ProductionDatabricks
 
Running Apache Spark on a High-Performance Cluster Using RDMA and NVMe Flash ...
Running Apache Spark on a High-Performance Cluster Using RDMA and NVMe Flash ...Running Apache Spark on a High-Performance Cluster Using RDMA and NVMe Flash ...
Running Apache Spark on a High-Performance Cluster Using RDMA and NVMe Flash ...Databricks
 
SQL on everything, in memory
SQL on everything, in memorySQL on everything, in memory
SQL on everything, in memoryJulian Hyde
 

What's hot (20)

Optimizing Apache Spark Throughput Using Intel Optane and Intel Memory Drive...
 Optimizing Apache Spark Throughput Using Intel Optane and Intel Memory Drive... Optimizing Apache Spark Throughput Using Intel Optane and Intel Memory Drive...
Optimizing Apache Spark Throughput Using Intel Optane and Intel Memory Drive...
 
Deep Dive into Spark SQL with Advanced Performance Tuning with Xiao Li & Wenc...
Deep Dive into Spark SQL with Advanced Performance Tuning with Xiao Li & Wenc...Deep Dive into Spark SQL with Advanced Performance Tuning with Xiao Li & Wenc...
Deep Dive into Spark SQL with Advanced Performance Tuning with Xiao Li & Wenc...
 
Using Apache Arrow, Calcite, and Parquet to Build a Relational Cache
Using Apache Arrow, Calcite, and Parquet to Build a Relational CacheUsing Apache Arrow, Calcite, and Parquet to Build a Relational Cache
Using Apache Arrow, Calcite, and Parquet to Build a Relational Cache
 
DataStax: Extreme Cassandra Optimization: The Sequel
DataStax: Extreme Cassandra Optimization: The SequelDataStax: Extreme Cassandra Optimization: The Sequel
DataStax: Extreme Cassandra Optimization: The Sequel
 
Oracle SQL, PL/SQL Performance tuning
Oracle SQL, PL/SQL Performance tuningOracle SQL, PL/SQL Performance tuning
Oracle SQL, PL/SQL Performance tuning
 
SQL for Data Science Tutorial | Data Science Tutorial | Edureka
SQL for Data Science Tutorial | Data Science Tutorial | EdurekaSQL for Data Science Tutorial | Data Science Tutorial | Edureka
SQL for Data Science Tutorial | Data Science Tutorial | Edureka
 
Spark Summit East 2017: Apache spark and object stores
Spark Summit East 2017: Apache spark and object storesSpark Summit East 2017: Apache spark and object stores
Spark Summit East 2017: Apache spark and object stores
 
Ibm과 nvidia가 제안하는 딥러닝 플랫폼
Ibm과 nvidia가 제안하는 딥러닝 플랫폼Ibm과 nvidia가 제안하는 딥러닝 플랫폼
Ibm과 nvidia가 제안하는 딥러닝 플랫폼
 
Hadoop Overview kdd2011
Hadoop Overview kdd2011Hadoop Overview kdd2011
Hadoop Overview kdd2011
 
Apache Spark 2.0: A Deep Dive Into Structured Streaming - by Tathagata Das
Apache Spark 2.0: A Deep Dive Into Structured Streaming - by Tathagata Das Apache Spark 2.0: A Deep Dive Into Structured Streaming - by Tathagata Das
Apache Spark 2.0: A Deep Dive Into Structured Streaming - by Tathagata Das
 
Facebook Presto presentation
Facebook Presto presentationFacebook Presto presentation
Facebook Presto presentation
 
Monitor Apache Spark 3 on Kubernetes using Metrics and Plugins
Monitor Apache Spark 3 on Kubernetes using Metrics and PluginsMonitor Apache Spark 3 on Kubernetes using Metrics and Plugins
Monitor Apache Spark 3 on Kubernetes using Metrics and Plugins
 
C* Summit 2013: The World's Next Top Data Model by Patrick McFadin
C* Summit 2013: The World's Next Top Data Model by Patrick McFadinC* Summit 2013: The World's Next Top Data Model by Patrick McFadin
C* Summit 2013: The World's Next Top Data Model by Patrick McFadin
 
Optimizing Delta/Parquet Data Lakes for Apache Spark
Optimizing Delta/Parquet Data Lakes for Apache SparkOptimizing Delta/Parquet Data Lakes for Apache Spark
Optimizing Delta/Parquet Data Lakes for Apache Spark
 
2021 04-20 apache arrow and its impact on the database industry.pptx
2021 04-20  apache arrow and its impact on the database industry.pptx2021 04-20  apache arrow and its impact on the database industry.pptx
2021 04-20 apache arrow and its impact on the database industry.pptx
 
How to Analyze and Tune MySQL Queries for Better Performance
How to Analyze and Tune MySQL Queries for Better PerformanceHow to Analyze and Tune MySQL Queries for Better Performance
How to Analyze and Tune MySQL Queries for Better Performance
 
Making Structured Streaming Ready for Production
Making Structured Streaming Ready for ProductionMaking Structured Streaming Ready for Production
Making Structured Streaming Ready for Production
 
Running Apache Spark on a High-Performance Cluster Using RDMA and NVMe Flash ...
Running Apache Spark on a High-Performance Cluster Using RDMA and NVMe Flash ...Running Apache Spark on a High-Performance Cluster Using RDMA and NVMe Flash ...
Running Apache Spark on a High-Performance Cluster Using RDMA and NVMe Flash ...
 
SQL on everything, in memory
SQL on everything, in memorySQL on everything, in memory
SQL on everything, in memory
 
Set, merge, and update
Set, merge, and updateSet, merge, and update
Set, merge, and update
 

Similar to How to make the fastest Router in Python

AWS Hadoop and PIG and overview
AWS Hadoop and PIG and overviewAWS Hadoop and PIG and overview
AWS Hadoop and PIG and overviewDan Morrill
 
Reitit - Clojure/North 2019
Reitit - Clojure/North 2019Reitit - Clojure/North 2019
Reitit - Clojure/North 2019Metosin Oy
 
Web program-peformance-optimization
Web program-peformance-optimizationWeb program-peformance-optimization
Web program-peformance-optimizationxiaojueqq12345
 
Thumbtack Expertise Days # 5 - Javaz
Thumbtack Expertise Days # 5 - JavazThumbtack Expertise Days # 5 - Javaz
Thumbtack Expertise Days # 5 - JavazAlexey Remnev
 
Scala45 spray test
Scala45 spray testScala45 spray test
Scala45 spray testkopiczko
 
[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기NAVER D2
 
High-level Programming Languages: Apache Pig and Pig Latin
High-level Programming Languages: Apache Pig and Pig LatinHigh-level Programming Languages: Apache Pig and Pig Latin
High-level Programming Languages: Apache Pig and Pig LatinPietro Michiardi
 
Cakefest 2010: API Development
Cakefest 2010: API DevelopmentCakefest 2010: API Development
Cakefest 2010: API DevelopmentAndrew Curioso
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryTatsuhiko Miyagawa
 
Presto anatomy
Presto anatomyPresto anatomy
Presto anatomyDongmin Yu
 
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...Flink Forward
 
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...Flink Forward
 
Using and scaling Rack and Rack-based middleware
Using and scaling Rack and Rack-based middlewareUsing and scaling Rack and Rack-based middleware
Using and scaling Rack and Rack-based middlewareAlona Mekhovova
 
Переход на Scala: босиком по граблям
Переход на Scala: босиком по граблямПереход на Scala: босиком по граблям
Переход на Scala: босиком по граблямSveta Bozhko
 
Logstash-Elasticsearch-Kibana
Logstash-Elasticsearch-KibanaLogstash-Elasticsearch-Kibana
Logstash-Elasticsearch-Kibanadknx01
 
Introduction To Groovy 2005
Introduction To Groovy 2005Introduction To Groovy 2005
Introduction To Groovy 2005Tugdual Grall
 

Similar to How to make the fastest Router in Python (20)

AWS Hadoop and PIG and overview
AWS Hadoop and PIG and overviewAWS Hadoop and PIG and overview
AWS Hadoop and PIG and overview
 
Reitit - Clojure/North 2019
Reitit - Clojure/North 2019Reitit - Clojure/North 2019
Reitit - Clojure/North 2019
 
Laravel
LaravelLaravel
Laravel
 
Web program-peformance-optimization
Web program-peformance-optimizationWeb program-peformance-optimization
Web program-peformance-optimization
 
Thumbtack Expertise Days # 5 - Javaz
Thumbtack Expertise Days # 5 - JavazThumbtack Expertise Days # 5 - Javaz
Thumbtack Expertise Days # 5 - Javaz
 
Scala45 spray test
Scala45 spray testScala45 spray test
Scala45 spray test
 
router-simple.cr
router-simple.crrouter-simple.cr
router-simple.cr
 
[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기
 
High-level Programming Languages: Apache Pig and Pig Latin
High-level Programming Languages: Apache Pig and Pig LatinHigh-level Programming Languages: Apache Pig and Pig Latin
High-level Programming Languages: Apache Pig and Pig Latin
 
Cakefest 2010: API Development
Cakefest 2010: API DevelopmentCakefest 2010: API Development
Cakefest 2010: API Development
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
 
Presto anatomy
Presto anatomyPresto anatomy
Presto anatomy
 
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...
 
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...
Flink Forward San Francisco 2019: Build a Table-centric Apache Flink Ecosyste...
 
Intro to PSGI and Plack
Intro to PSGI and PlackIntro to PSGI and Plack
Intro to PSGI and Plack
 
Using and scaling Rack and Rack-based middleware
Using and scaling Rack and Rack-based middlewareUsing and scaling Rack and Rack-based middleware
Using and scaling Rack and Rack-based middleware
 
Переход на Scala: босиком по граблям
Переход на Scala: босиком по граблямПереход на Scala: босиком по граблям
Переход на Scala: босиком по граблям
 
Logstash-Elasticsearch-Kibana
Logstash-Elasticsearch-KibanaLogstash-Elasticsearch-Kibana
Logstash-Elasticsearch-Kibana
 
Rack
RackRack
Rack
 
Introduction To Groovy 2005
Introduction To Groovy 2005Introduction To Groovy 2005
Introduction To Groovy 2005
 

More from kwatch

Migr8.rb チュートリアル
Migr8.rb チュートリアルMigr8.rb チュートリアル
Migr8.rb チュートリアルkwatch
 
なんでもID
なんでもIDなんでもID
なんでもIDkwatch
 
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方kwatch
 
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方kwatch
 
O/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐO/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐkwatch
 
正規表現リテラルは本当に必要なのか?
正規表現リテラルは本当に必要なのか?正規表現リテラルは本当に必要なのか?
正規表現リテラルは本当に必要なのか?kwatch
 
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)kwatch
 
DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!kwatch
 
PHPとJavaScriptにおけるオブジェクト指向を比較する
PHPとJavaScriptにおけるオブジェクト指向を比較するPHPとJavaScriptにおけるオブジェクト指向を比較する
PHPとJavaScriptにおけるオブジェクト指向を比較するkwatch
 
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?kwatch
 
Fantastic DSL in Python
Fantastic DSL in PythonFantastic DSL in Python
Fantastic DSL in Pythonkwatch
 
What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策
What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策
What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策kwatch
 
PHP5.5新機能「ジェネレータ」初心者入門
PHP5.5新機能「ジェネレータ」初心者入門PHP5.5新機能「ジェネレータ」初心者入門
PHP5.5新機能「ジェネレータ」初心者入門kwatch
 
Pretty Good Branch Strategy for Git/Mercurial
Pretty Good Branch Strategy for Git/MercurialPretty Good Branch Strategy for Git/Mercurial
Pretty Good Branch Strategy for Git/Mercurialkwatch
 
Oktest - a new style testing library for Python -
Oktest - a new style testing library for Python -Oktest - a new style testing library for Python -
Oktest - a new style testing library for Python -kwatch
 
文字列結合のベンチマークをいろんな処理系でやってみた
文字列結合のベンチマークをいろんな処理系でやってみた文字列結合のベンチマークをいろんな処理系でやってみた
文字列結合のベンチマークをいろんな処理系でやってみたkwatch
 
I have something to say about the buzz word "From Java to Ruby"
I have something to say about the buzz word "From Java to Ruby"I have something to say about the buzz word "From Java to Ruby"
I have something to say about the buzz word "From Java to Ruby"kwatch
 
Cより速いRubyプログラム
Cより速いRubyプログラムCより速いRubyプログラム
Cより速いRubyプログラムkwatch
 
Javaより速いLL用テンプレートエンジン
Javaより速いLL用テンプレートエンジンJavaより速いLL用テンプレートエンジン
Javaより速いLL用テンプレートエンジンkwatch
 
Underlaying Technology of Modern O/R Mapper
Underlaying Technology of Modern O/R MapperUnderlaying Technology of Modern O/R Mapper
Underlaying Technology of Modern O/R Mapperkwatch
 

More from kwatch (20)

Migr8.rb チュートリアル
Migr8.rb チュートリアルMigr8.rb チュートリアル
Migr8.rb チュートリアル
 
なんでもID
なんでもIDなんでもID
なんでもID
 
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
 
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
 
O/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐO/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐ
 
正規表現リテラルは本当に必要なのか?
正規表現リテラルは本当に必要なのか?正規表現リテラルは本当に必要なのか?
正規表現リテラルは本当に必要なのか?
 
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
 
DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!
 
PHPとJavaScriptにおけるオブジェクト指向を比較する
PHPとJavaScriptにおけるオブジェクト指向を比較するPHPとJavaScriptにおけるオブジェクト指向を比較する
PHPとJavaScriptにおけるオブジェクト指向を比較する
 
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
 
Fantastic DSL in Python
Fantastic DSL in PythonFantastic DSL in Python
Fantastic DSL in Python
 
What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策
What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策
What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策
 
PHP5.5新機能「ジェネレータ」初心者入門
PHP5.5新機能「ジェネレータ」初心者入門PHP5.5新機能「ジェネレータ」初心者入門
PHP5.5新機能「ジェネレータ」初心者入門
 
Pretty Good Branch Strategy for Git/Mercurial
Pretty Good Branch Strategy for Git/MercurialPretty Good Branch Strategy for Git/Mercurial
Pretty Good Branch Strategy for Git/Mercurial
 
Oktest - a new style testing library for Python -
Oktest - a new style testing library for Python -Oktest - a new style testing library for Python -
Oktest - a new style testing library for Python -
 
文字列結合のベンチマークをいろんな処理系でやってみた
文字列結合のベンチマークをいろんな処理系でやってみた文字列結合のベンチマークをいろんな処理系でやってみた
文字列結合のベンチマークをいろんな処理系でやってみた
 
I have something to say about the buzz word "From Java to Ruby"
I have something to say about the buzz word "From Java to Ruby"I have something to say about the buzz word "From Java to Ruby"
I have something to say about the buzz word "From Java to Ruby"
 
Cより速いRubyプログラム
Cより速いRubyプログラムCより速いRubyプログラム
Cより速いRubyプログラム
 
Javaより速いLL用テンプレートエンジン
Javaより速いLL用テンプレートエンジンJavaより速いLL用テンプレートエンジン
Javaより速いLL用テンプレートエンジン
 
Underlaying Technology of Modern O/R Mapper
Underlaying Technology of Modern O/R MapperUnderlaying Technology of Modern O/R Mapper
Underlaying Technology of Modern O/R Mapper
 

Recently uploaded

Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vázquez
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...apidays
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024The Digital Insurer
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsNanddeep Nachan
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...apidays
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdflior mazor
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businesspanagenda
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbuapidays
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 

Recently uploaded (20)

Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 

How to make the fastest Router in Python

  • 1. How To Make The Fastest Router In Python Makoto Kuwata https://github.com/kwatch/ PloneConference 2018 Tokyo
  • 2. Abstract ‣ TL;DR ‣ You can make router much faster (max: x10) ‣ Requirements ‣ Python3 ‣ Experience of Web Application Framework (Django, Flask, Plone, etc) ‣ Sample Code ‣ https://github.com/kwatch/router-sample/ ‣ https://github.com/kwatch/keight/tree/python/ (Framework)
  • 3. Table of Contents ‣ What is Router? ‣ Linear Search ‣ Naive / Prefix String / Fixed Path Dictionary ‣ Regular Expression ‣ Naive / Smart / Optimized ‣ State Machine ‣ Conclusion
  • 5. What is Router? ‣ Router is a component of web app framework (WAF). ‣ Router determines request handler according to request method and request path.
 Handler A App Server Handler B Handler C Client : HTTP Request : HTTP Response WSGI App Server Side Router determines "which handler?"
  • 6. Request Handler Example class BooksAPI(RequestHandler): with on.path('/api/books/'): @on('GET') def do_index(self): return {"action": "index"} @on('POST') def do_create(self): return {"action": "create"} .... ← handler class ← URL Path ← request method ← handler func ← request method ← handler func
  • 7. Request Handler Example .... with on.path('/api/books/{id:int}'): @on('GET') def do_show(self, id): return {"action": "show", "id": id} @on('PUT') def do_update(self, id): return {"action": "update", "id": id} @on('DELETE') def do_delete(self, id): return {"action": "delete", "id": id} ← URL Path ← request method ← handler func ← request method ← handler func ← request method ← handler func
  • 8. Mapping Example: Request to Handler mapping_table = [ ## method path class func ("GET" , r"/api/books/" , BooksAPI , do_index), ("POST" , r"/api/books/" , BooksAPI , do_create),
 ("GET" , r"/api/books/(d+)" , BooksAPI , do_show), ("PUT" , r"/api/books/(d+)" , BooksAPI , do_update),
 ("DELETE", r"/api/books/(d+)" , BooksAPI , do_delete),
 ("GET" , r"/api/orders/" , OrdersAPI, do_index),
 ("POST" , r"/api/orders/" , OrdersAPI, do_create),
 ("GET" , r"/api/orders/(d+)", OrdersAPI, do_show), ("PUT" , r"/api/orders/(d+)", OrdersAPI, do_update),
 ("DELETE", r"/api/orders/(d+)", OrdersAPI, do_delete),
 .... ]
  • 9. Mapping Example: Request to Handler mapping_list = [ ## path class {method: func} (r"/api/books/" , BooksAPI , {"GET": do_index, "POST": do_create}), (r"/api/books/(d+)" , BooksAPI , {"GET": do_show, "PUT": do_update, "DELETE": do_delete}),
 (r"/api/orders/" , OrdersAPI, {"GET": do_index, "POST": do_create}), (r"/api/orders/(d+)", OrdersAPI, {"GET": do_show, "PUT": do_update, "DELETE": do_delete}),
 .... ] Same information in different format
  • 10. Router Example >>> router = Router(mapping_list) >>> router.lookup("GET", "/api/books/") (BooksAPI, do_index, []) >>> router.lookup("GET", "/api/books/123") (BooksAPI, do_show, [123])
  • 11. Router Example ### 404 Not Found >>> router.lookup("GET", "/api/books/123/comments") (None, None, None) ### 405 Method Not Allowed >>> router.lookup("POST", "/api/books/123") (BooksAPI, None, [123])
  • 13. Linear Search mapping = [ (r"/api/books/" , BooksAPI , {"GET": do_index, "POST": do_create}), (r"/api/books/(d+)" , BooksAPI , {"GET": do_show, "PUT": do_update, "DELETE": do_delete}),
 (r"/api/orders/" , OrdersAPI, {"GET": do_index, "POST": do_create}), (r"/api/orders/(d+)", OrdersAPI, {"GET": do_show, "PUT": do_update, "DELETE": do_delete}),
 .... ]
  • 14. Router Class class LinearNaiveRouter(Router): def __init__(self, mapping): self._mapping_list = [ (compile_path(path), klass, funcs) for path, klass, funcs in mapping ] def lookup(req_meth, req_path): for rexp, klass, funcs in self._mapping_list: m = rexp.match(req_path) if m: params = [ int(v) for v in m.groups() ] func = funcs.get(req_meth) return klass, func, params return None, None, None
  • 15. Benchmark (Data) mapping_list = [ (r'/api/aaa' , DummyAPI, {"GET": ...}), (r'/api/aaa/{id:int}', DummyAPI, {"GET": ...}), (r'/api/bbb' , DummyAPI, {"GET": ...}), (r'/api/bbb/{id:int}', DummyAPI, {"GET": ...}), .... (r'/api/yyy' , DummyAPI, {"GET": ...}), (r'/api/yyy/{id:int}', DummyAPI, {"GET": ...}), (r'/api/zzz' , DummyAPI, {"GET": ...}), (r'/api/zzz/{id:int}', DummyAPI, {"GET": ...}), ] ### Benchmark environment: ### AWS EC2 t3.nano, Ubuntu 18.04, Python 3.6.6 See sample code for details
  • 16. Benchmark 0 10 20 30 40 50 Linear Naive Seconds (1M Requests) /api/aaa /api/aaa/{id} /api/zzz /api/zzz/{id} sec SlowerFaster very fast on top of list (/api/aaa, /api/aaa/{id}) very slow on bottom of list (/api/zzz, /api/zzz/{id})
  • 17. Pros. Cons. Pros & Cons ✗ Very slow when many mapping entries exist. ✓ Easy to understand and implement
  • 19. Prefix String mapping_list = [ ("/books" , r"/books" , BooksAPI , {"GET": ...}),
 ("/books/" , r"/books/(d+)" , BooksAPI , {"GET": ...}),
 ("/orders" , r"/orders" , OrdersAPI, {"GET": ...}),
 ("/orders/", r"/orders/(d+)", OrdersAPI, {"GET": ...}),
 ] for prefix, rexp, klass, funcs in mapping: if not "/api/orders/123".startswith(prefix): continue m = rexp.match("/api/orders/123") if m: ... Much faster than rexp.match() (replace expensive operation with cheap operation) Prefix strings
  • 20. Router Class def prefix_str(s): return s.split('{', 1)[0] class PrefixLinearRouter(Router): def __init__(self, mapping): for path, klass, funcs in mapping: prefix = prefix_str(path) rexp = compile_path(path) t = (prefix, rexp, klass, funcs) self._mapping_list.append(t) ...
  • 21. Router Class .... def lookup(req_meth, req_path): for prefix, rexp, klass, funcs in self._mapping: if not req_path.startswith(prefix): continue m = rexp.match(req_path) if m: params = [ int(v) for v in m.groups() ] func = funcs.get(req_meth) return klass, func, params return None, None, None Much faster than rexp.match()
  • 22. Benchmark 0 10 20 30 40 50 Linear Naive Prefix Str Seconds (1M Requests) /api/aaa /api/aaa/{id} /api/zzz /api/zzz/{id} sec SlowerFaster about twice as fast as naive implementation
  • 23. Pros. Cons. Pros & Cons ✗ Still slow when many mapping entries exist. ✓ Makes linear search faster. ✓ Easy to understand and implement.
  • 25. Fixed Path Dictionary ## variable path (contains one or more path parameters) mapping_list = [ ("/books" , r"/books" , BooksAPI , {"GET": ...}),
 ("/books/" , r"/books/(d+)" , BooksAPI , {"GET": ...}),
 ("/orders" , r"/orders" , OrdersAPI, {"GET": ...}),
 ("/orders/", r"/orders/(d+)", OrdersAPI, {"GET": ...}),
 ] ## fixed path (contains no path parameters) mapping_dict = { r"/books" : (BooksAPI , {"GET": ...}, []), r"/orders": (OrdersAPI, {"GET": ...}, []), } Use fixed path as key of dict Move fixed path to dict
  • 26. Router Class class FixedLinearRouter(object): def __init__(self, mapping): self._mapping_dict = {} self._mapping_list = [] for path, klass, funcs in mapping: if '{' not in path: self._mapping_dict[path] = (klass, funcs, [])
 else: prefix = prefix_str(path) rexp = compile_path(path) t = (prefix, rexp, klass, funcs) self._mapping_list.append(t) ....
  • 27. Router Class .... def lookup(req_meth, req_path): t = self._mapping_dict.get(req_path) if t: return t for prefix, rexp, klass, funcs in self._mapping_list:
 if not req_path.startswith(prefix) continue m = rexp.match(req_path) if m: params = [ int(v) for v in m.groups() ] func = funcs.get(req_meth) return klass, func, params return None, None, None Much faster than for-loop Number of entries are reduced
  • 28. Benchmark 0 10 20 30 40 50 Linear Naive Prefix Str Fixed Path Seconds (1M Requests) /api/aaa /api/aaa/{id} /api/zzz /api/zzz/{id} sec SlowerFaster super fast on fixed path! three times faster than naive implementation
  • 29. Pros. Cons. Pros & Cons ✗ Still slow when many mapping entries exist. ✓ Makes fixed path search super faster. ✓ Makes variable path search faster,
 because number of entries are reduced. ✓ Easy to understand and implement.
  • 30. Notice ‣ Don't use r"/api/v{version:int}". ‣ because all API paths are regarded as variable path. ‣ Instead, use r"/api/v1", r"/api/v2", ... ‣ in order to increase number of fixed path.
  • 32. Concatenate Regular Expressions mapping_list = { (r"/api/books/(d+)" , BooksAPI , {"GET": ...}),
 (r"/api/orders/(d+)", OrdersAPI, {"GET": ...}),
 (r"/api/users/(d+)" , UsersAPI , {"GET": ...}),
 ] arr = [ r"(?P<_0>^/api/books/(d+)$)", r"(?P<_1>^/api/orders/(d+)$)", r"(?P<_2>^/api/users/(d+)$)", ] all_rexp = re.compile("|".join(arr)) Named groups
  • 33. Matching m = all_rexp.match("/api/users/123") d = m.groupdict() #=> {"_0": None, # "_1": None, # "_2": "/api/users/123"} for k, v in d.items(): if v: i = int(v[1:]) # ex: "_2" -> 2 break _, klass, funcs, pos, nparams = mapping_list[i] arr = m.groups() #=> (None, None, None, None, # "/api/users/123", "123") params = arr[5:6] #=> {"123"}
  • 34. Router Class class NaiveRegexpRouter(Router): def __init__(self, mapping): self._mapping_dict = {} self._mapping_list = [] arr = []; i = 0; pos = 0 for path, klass, funcs in mapping: if '{' not in path: self._mapping_dict[path] = (klass, funcs, []) else: rexp = compile_path(path); pat = rexp.pattern arr.append("(?P<_%s>%s)" % (i, pat)) t = (klass, funcs, pos, path.count('{')) self._mapping_list.append(t) i += 1; pos += 1 + path.count('{') self._all_rexp = re.compile("|".join(arr))
  • 35. Router Class .... def lookup(req_meth, req_path): t = self._mapping_dict.get(req_path) if t: return t m = self._all_rexp.match(req_path) if m: for k, v in m.groupdict().items(): if v: i = int(v[1:]) break klass, funcs, pos, nparams = self._mapping_list[i]
 params = m.groups()[pos:pos+nparams] func = funcs.get(req_meth) return klass, func, params return None, None, None find index in list find param values
  • 36. Benchmark 0 10 20 30 40 50 Linear Regexp Naive Prefix Str Fixed Path Naive Seconds (1M Requests) /api/aaa /api/aaa/{id} /api/zzz /api/zzz/{id} sec SlowerFaster slower than linear search :(
  • 37. Pros. Cons. Pros & Cons ✗ Slower than linear search ✓ Nothing :(
  • 38. Notice $ python3 --version 3.4.5 $ python3 >>> import re >>> arr = ['^/(d+)$'] * 101 >>> re.compile("|".join(arr)) File "/opt/vs/python/3.4.5/lib/python3.4/sre_compile.py", line 579, in compile "sorry, but this version only supports 100 named groups" AssertionError: sorry, but this version only supports 100 named groups Python <= 3.4 limits number of groups in a regular expression, and no work around :(
  • 40. Improved Regular Expression mapping_list = { (r"/api/books/(d+)" , BooksAPI , {"GET": ...}), (r"/api/orders/(d+)" , OrdersAPI , {"GET": ...}), (r"/api/users/(d+)" , UsersAPI , {"GET": ...}), ] arr = [ r"^/api/books/(?:d+)($)", r"^/api/orders/(?:d+)($)", r"^/api/users/(?:d+)($)", ] all_rexp = re.compile("|".join(arr)) m = all_rexp.match("/api/users/123") arr = m.groups() #=> (None, None, "") i = arr.index("") #=> 2 t = mapping_list[i] #=> (r"/api/users/(d+)", # UsersAPI, {"GET": ...}) No more named groups Tuple is much light- weight than dict index() is faster than for-loop
  • 41. Router Class class SmartRegexpRouter(Router): def __init__(self, mapping): self._mapping_dict = {} self._mapping_list = [] arr = [] for path, klass, funcs in mapping: if '{' not in path: self._mapping_dict[path] = (klass, funcs, [])
 else: rexp = compile_path(path); pat = rexp.pattern
 arr.append(pat.replace("(", "(?:") .replace("$", "($)")) t = (rexp, klass, funcs) self._mapping_list.append(t)
 self._all_rexp = re.compile("|".join(arr))
  • 42. Router Class ... def lookup(req_meth, req_path): t = self._mapping_dict.get(req_path) if t: return t m = self._all_rexp.match(req_path) if m: i = m.groups().index("") rexp, klass, funcs = self._mapping_list[i] m2 = rexp.match(req_path) params = [ int(v) for v in m2.groups() ] func = funcs.get(req_meth) return klass, func, params return None, None, None Matching to find index in list Matching to get param values
  • 43. Benchmark 0 10 20 30 40 50 Linear Regexp Naive Prefix Str Fixed Path Naive Smart Seconds (1M Requests) /api/aaa /api/aaa/{id} /api/zzz /api/zzz/{id} sec SlowerFaster Difference between /api/aaa/{id} and /api/zzz/{id} is small
  • 44. Pros. Cons. Pros & Cons ✗ Slower when number of entries is small.
 (due to overhead of twice matching) ✗ May be difficult to debug large regular expression. ✓ Much faster than ever,
 especially when many mapping entries exist.
  • 46. Optimize Regular Expression ## before arr = [r"^/api/books/(?:d+)($)", r"^/api/orders/(?:d+)($)", r"^/api/users/(?:d+)($)"] all_rexp = re.compile("|".join(arr)) ### after arr = [r"^/api", r"(?:", "|".join([r"/books/(?:d+)($)", r"/orders/(?:d+)($)", r"/users/(?:d+)($)"]), r")?"] all_rexp = re.compile("|".join(arr))
  • 47. Router Class class OptimizedRegexpRouter(Router): def __init__(self, mapping): ## Code is too complicated to show here. ## Please download sample code from github. ## https://github.com/kwatch/router-sample/ def lookup(req_meth, req_path): ## nothing changed; same as previous section
  • 48. Benchmark 0 10 20 30 40 50 Linear Regexp Naive Prefix Str Fixed Path Naive Smart Optimized Seconds (1M Requests) /api/aaa /api/aaa/{id} /api/zzz /api/zzz/{id} sec SlowerFaster A little faster on /api/zzz/{id}
  • 49. Pros. Cons. Pros & Cons ✗ Performance benefit is very small (on Python). ✗ Rather difficult to implement and debug. ✓ A little faster than smart regular expression
 when a lot of variable paths exist.
  • 52. State Machine: Definition path = "/api/books" transition = { "api": { "books": { None: (BooksAPI, {"GET": do_index, ...}), }, }, } >>> transition["api"]["books"][None] (BooksAPI, {"GET": do_index, ...}) >>> transition["api"]["users"][None] KeyError: 'users' Use None as terminator (mark of accepted status)
  • 53. State Machine: Definition path = "/api/books/{id:int}" transition = { "api": { "books": { None: (BooksAPI, {"GET": do_index, ...}), 1: { None: (BooksAPI, {"GET": do_show, ...}), }, }, }, } >>> transition["api"]["books"][1][None] (BooksAPI, {"GET": do_index, ...}) 1 represents int parameter, 2 represents str parameter.
  • 54. State Machine: Transition def find(req_path): req_path = req_path.lstrip('/') #ex: "/a/b/c" -> "a/b/c" items = req_path.split('/') #ex: "a/b/c" -> ["a","b","c"] d = transition; params = [] for s in items: if s in d: d = d[s] elif 1 in d: d = d[1]; params.append(int(s)) elif 2 in d: d = d[2]; params.append(str(s)) else: return None if None not in d: return None klass, funcs = d[None] return klass, funcs, params >>> find("/api/books/123") (BooksAPI, {"GET": do_index, ...}, [123])
  • 55. Router Class class StateMachineRouter(Router): def __init__(self, mapping): self._mapping_dict = {} self._mapping_list = [] self._transition = {} for path, klass, funcs in mapping: if '{' not in path: self._mapping_dict[path] = (klass, funcs, [])
 else: self._register(path, klass, funcs)
  • 56. Router Class ... PARAM_TYPES = {"int": 1, "str": 2} def _register(self, path, klass, funcs): ptypes = self.PARAM_TYPES d = self._transition for s in path[1:].split('/'): key = s if s[0] == "{" and s[-1] == "}": ## ex: "{id:int}" -> ("id", "int") pname, ptype = s[1:-1].split(':', 1) key = ptypes.get(ptype) or ptypes["str"] d = d.setdefault(key, {}) d[None] = (klass, funcs)
  • 57. Router Class ... def lookup(self, req_meth, req_path): d = self._transition params = [] for s in req_path[1:].split('/'): if s in d: d = d[s] elif 1 in d: d = d[1]; params.append(int(s)) elif 2 in d: d = d[2]; params.append(str(s)) else: return None, None, None if None in d: klass, funcs = d[None] func = funcs.get(req_meth) return klass, func, params return None, None, None
  • 58. Benchmark 0 10 20 30 40 50 Linear Regexp StateMachine Naive Prefix Str Fixed Path Naive Smart Optimized Seconds (1M Requests) /api/aaa /api/aaa/{id} /api/zzz /api/zzz/{id} sec SlowerFaster /api/aaa/{id} and /api/zzz/{id} are same performance
  • 59. Benchmark (PyPy3.5) 0 10 20 30 40 50 Linear Regexp StateMachine Naive Prefix Str Fixed Path Naive Smart Optimized Seconds (1M Requests) /api/aaa /api/aaa/{id} /api/zzz /api/zzz/{id} sec SlowerFaster Regular Expression is very slow in PyPy3.5 String operation is very fast because JIT friendly
  • 60. Benchmark (PyPy3.5) 0 1 2 3 4 5 Linear Regexp StateMachine Naive Prefix Str Fixed Path Naive Smart Optimized Seconds (1M Requests) /api/aaa /api/aaa/{id} /api/zzz /api/zzz/{id} sec SlowerFaster The fastest method due to Regexp-free (= JIT friendly) A little slower than StateMachine because containing Regexp
  • 61. Pros. Cons. Pros & Cons ✗ Not support complicated pattern. ✗ Requires some effort to support URL path suffix (ex: /api/books/123.json). ✓ Performance champion in routing area. ✓ Much faster in PyPy3.5, due to regexp-free. JIT friendly!
  • 63. Conclusion ‣ Linear Search is slow. ‣ Prefix string and Fixed path dict make it faster. ‣ Regular expression is very fast. ‣ Do your best to avoid named group (or named caption). ‣ State Machine is the fastest method in Python. ‣ Especially in PyPy3, due to regexp-free (= JIT friendly).
  • 65. My Products ‣ Benchmarker.py Awesome benchmarking utility. https://pythonhosted.org/Benchmarker/ ‣ Oktest.py New generation of testing framework. https://pythonhosted.org/Oktest/ ‣ PyTenjin Super fast and feature-rich template engine. https://www.kuwata-lab.com/tenjin/pytenjin-users-guide.html https://bit.ly/tenjinpy_slide (presentation)