-
-
Save rcarmo/5394708 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
""" | |
Main application script | |
Created by: Rui Carmo | |
License: MIT (see LICENSE for details) | |
""" | |
import os, sys | |
sys.path.append('lib') | |
import bottle, routes | |
application = bottle.app() |
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
""" | |
Model shims | |
Created by: Rui Carmo | |
""" | |
import os, sys, random, time, datetime | |
FIRST_NAMES = ["Emily", "Jacob", "Michael", "Emma", "Joshua", "Madison", "Matthew", "Hannah", "Andrew", "Olivia", "Cletus", "Warner", "Sarah", "Billy", "Brittany", "Daniel", "David", "Cristman", "Colin", "Royalle"] | |
LAST_NAMES = ["Aaron", "Bolingbroke", "Crounse", "Duff", "Drake", "Downs", "Driver", "Jasper", "Jetter", "O'Leary", "O'Malley", "Neville", "Towers", "Tripp", "Trull", "Wakefield", "Waller", "Badger", "Bagley", "Baker"] | |
STATES = ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"] | |
db = {} | |
_date_range = [ | |
time.mktime(datetime.datetime(1971,1,1).timetuple()), | |
time.mktime(datetime.datetime(2000,1,1).timetuple()) | |
] | |
def random_date(): | |
start = _date_range[0] | |
end = _date_range[1] | |
return random.randrange(start, end, 38400) | |
class Customers: | |
customers = [] | |
def __init__(self): | |
for i in range(0,200): | |
self.customers.append({ | |
'first_name': random.choice(FIRST_NAMES), | |
'last_name': random.choice(LAST_NAMES), | |
'state': random.choice(STATES), | |
'birth_date': random_date(), | |
'id': i | |
}) | |
def find_all(self): | |
return self.customers | |
def find_by_id(self, i): | |
return self.customers[i] | |
def save(self, customer): | |
self.customers.append(customer) | |
def delete(self, id): | |
del self.customers[id] |
<!DOCTYPE html> | |
<html xmlns:tal="http://xml.zope.org/namespaces/tal"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
<title>Customer List</title> | |
<style> | |
body { | |
color: #6F6F6F; | |
font-family: 'Lucida Sans', 'Helvetica', | |
'Sans-serif', 'sans'; | |
font-size: 9pt; | |
line-height: 1.8em; | |
padding: 10px; | |
margin: 10px; | |
} | |
h1 { | |
color: #E9601A; | |
font-size: 14pt; | |
} | |
table.decorated { | |
width: 100%; | |
border-collapse: collapse; | |
} | |
table.decorated td { | |
padding-right: 5px; | |
padding-left: 5px; | |
} | |
table.decorated thead td { | |
font-weight: bold; | |
color: #FFF; | |
background-color: #CCC; | |
} | |
table.decorated tbody td { | |
border: 1px solid #CCC; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Customer List</h1> | |
<table class="decorated"> | |
<thead> | |
<tr> | |
<td>First Name</td> | |
<td>Last Name</td> | |
<td>State</td> | |
<td>Birth Date</td> | |
<td>Options</td> | |
</tr> | |
</thead> | |
<tbody> | |
{{ range . }} | |
<tr> | |
<td>{{ .FirstName }}</td> | |
<td>{{ .LastName }}</td> | |
<td>{{ .State }}</td> | |
<td>{{ .BirthDate }}</td> | |
<td><a href="/delete/{{ .Id }}">Delete</a></td> | |
</tr> | |
{{ end }} | |
</tbody> | |
</table> | |
</body> | |
</html> |
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
""" | |
Static routes | |
Created by: Rui Carmo | |
License: MIT (see LICENSE for details) | |
""" | |
import os, sys, logging | |
from bottle import route, view, response, redirect | |
from models import Customers | |
log = logging.getLogger() | |
c = Customers() | |
@route('/customerList') | |
@view('index') | |
def index(): | |
global c | |
return {'customers': c.find_all()} | |
@route('/delete/<id:int>') | |
def delete(id): | |
"""Static file handler""" | |
global c | |
c.delete(id) | |
redirect('/') |
package main | |
import ( | |
"time" | |
"math/rand" | |
"net/http" | |
"html/template" | |
"runtime" | |
) | |
var FIRST_NAMES = []string{"Emily", "Jacob", "Michael", "Emma", "Joshua", "Madison", "Matthew", "Hannah", "Andrew", "Olivia", "Cletus", "Warner", "Sarah", "Billy", "Brittany", "Daniel", "David", "Cristman", "Colin", "Royalle"} | |
var LAST_NAMES = []string{"Aaron", "Bolingbroke", "Crounse", "Duff", "Drake", "Downs", "Driver", "Jasper", "Jetter", "O'Leary", "O'Malley", "Neville", "Towers", "Tripp", "Trull", "Wakefield", "Waller", "Badger", "Bagley", "Baker"} | |
var STATES = []string{"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"} | |
var page_html = template.Must(template.ParseFiles("page.html")) | |
type Customer struct { | |
Id int | |
FirstName string | |
LastName string | |
State string | |
BirthDate time.Time | |
} | |
var count = 1 | |
var customers = make([]Customer,200) | |
func random_first_name() string { | |
return FIRST_NAMES[rand.Intn(len(FIRST_NAMES))] | |
} | |
func random_last_name() string { | |
return LAST_NAMES[rand.Intn(len(LAST_NAMES))] | |
} | |
func random_state() string { | |
return STATES[rand.Intn(len(STATES))] | |
} | |
func random_int(min int, max int) int { | |
return min + rand.Intn(max-min) | |
} | |
func random_date() time.Time { | |
var now int64 = time.Now().Unix() | |
return time.Unix(int64(rand.Intn(int(now))),0) | |
} | |
func new_customer(n int) Customer { | |
return Customer { | |
Id : n, | |
FirstName : random_first_name(), | |
LastName : random_last_name(), | |
State : random_state(), | |
BirthDate : random_date(), | |
} | |
} | |
func handler(w http.ResponseWriter, r *http.Request) { | |
if err := page_html.Execute(w, customers); err != nil { | |
http.Error(w, err.Error(), http.StatusInternalServerError) | |
return | |
} | |
} | |
func main() { | |
runtime.GOMAXPROCS(8) | |
rand.Seed(time.Now().UTC().UnixNano()) | |
for i := 0; i < len(customers); i++ { | |
customers[i] = new_customer(i) | |
} | |
http.HandleFunc("/customerList", handler) | |
http.ListenAndServe(":8082", nil) | |
} |
[uwsgi] | |
chdir = . | |
pythonpath = /Library/Python/2.7/site-packages/greenlet-0.4.0-py2.7-macosx-10.8-intel.egg:/Library/Python/2.7/site-packages | |
wsgi = benchmark:application | |
procname-prefix = bottle-uwsgi- | |
main_plugin = python,gevent | |
http-socket = 0.0.0.0:8082 | |
loop = gevent | |
async = 50 | |
enable-threads = true | |
listen = 1024 | |
harakiri = 10 | |
optimize = 2 | |
master = True | |
processes = 8 | |
disable-logging = True | |
#logto = /dev/null | |
no-default-app = False | |
auto-procname = True | |
limit-as = 90 | |
buffer-size = 2048 | |
socket-timeout = 30 | |
post-buffering = 1024 | |
limit-post = 1024 | |
thread-stacksize = 64 |
%import time | |
<!DOCTYPE html> | |
<html xmlns:tal="http://xml.zope.org/namespaces/tal"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
<title>Customer List</title> | |
<style> | |
body { | |
color: #6F6F6F; | |
font-family: 'Lucida Sans', 'Helvetica', | |
'Sans-serif', 'sans'; | |
font-size: 9pt; | |
line-height: 1.8em; | |
padding: 10px; | |
margin: 10px; | |
} | |
h1 { | |
color: #E9601A; | |
font-size: 14pt; | |
} | |
table.decorated { | |
width: 100%; | |
border-collapse: collapse; | |
} | |
table.decorated td { | |
padding-right: 5px; | |
padding-left: 5px; | |
} | |
table.decorated thead td { | |
font-weight: bold; | |
color: #FFF; | |
background-color: #CCC; | |
} | |
table.decorated tbody td { | |
border: 1px solid #CCC; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Customer List</h1> | |
<table class="decorated"> | |
<thead> | |
<tr> | |
<td>First Name</td> | |
<td>Last Name</td> | |
<td>State</td> | |
<td>Birth Date</td> | |
<td>Options</td> | |
</tr> | |
</thead> | |
<tbody> | |
%for c in customers: | |
<tr> | |
%for k in ['first_name','last_name','state']: | |
<td>{{c[k]}}</td> | |
%end | |
<td>{{time.strftime('%d-%m-%Y',time.gmtime(c['birth_date']))}}</td> | |
<td><a href="/delete/{{c['id']}}">Delete</a></td> | |
</tr> | |
%end | |
</tbody> | |
</table> | |
</body> | |
</html> |
The full Python code is fairly extensive. I can publish the model, route, etc., but it's a full MVC framework tree...
Done. At least the interesting bits, the ordering is a bit muddled, though. This is an MVC Bottle app that uses uwsgi and gevent (I've included the uwsgi.ini file).
Anyway, I'd love to understand why Go fares so poorly. It's the simplest thing possible, nearly a straight port of the Python code.
Some background:
We've been doing exactly the same pattern (HTML template filled in with precomputed random data) in a number of languages (Java, using jetty, netty, etc.), ASP.NET, Python (bottle gevent, tornado), and soon Ruby, LuaJIT, etc., and Go seemed like the perfect candidate to best them all.
I tossed in Python and Go into the crucible and was a bit amazed that on an 8-core box Go had such poor performance - we started with 1.0.3, and I re-did the benchmark on my laptop with Go 1.1 (those are the figures posted above). I've also been looking into PyPy and Erlang and would like to eventually publish the whole thing somewhere, but not with such a disparity.
On my MBP Retina (8 core i7) I get these numbers:
running python with :
bottle.run(port=8082, server='gevent', quiet=True, log=None)
python:
wrk -t 10 -r 1000 http://localhost:8082/customerList
Making 1000 requests to http://localhost:8082/customerList
10 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 63.33ms 22.68ms 117.57ms 78.55%
Req/Sec 0.00 0.00 0.00 100.00%
1000 requests in 6.00s, 50.30MB read
Requests/sec: 166.59
Transfer/sec: 8.38MB
go1.0.3:
wrk -t 10 -r 1000 http://localhost:8082/customerList
Making 1000 requests to http://localhost:8082/customerList
10 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 36.20ms 1.96ms 45.69ms 82.63%
Req/Sec 0.00 0.00 0.00 100.00%
1000 requests in 3.63s, 65.90MB read
Requests/sec: 275.17
Transfer/sec: 18.13MB
go1.1:
wrk -t 10 -r 1000 http://localhost:8082/customerList
Making 1000 requests to http://localhost:8082/customerList
10 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 17.11ms 1.86ms 26.91ms 82.20%
Req/Sec 0.00 0.00 0.00 100.00%
1000 requests in 1.73s, 54.22MB read
Requests/sec: 579.20
Transfer/sec: 31.41MB
Python code, please.