Przeglądaj źródła

Merge pull request #392 from methane/python-update

Python update for round 7
Michael Hixson 12 lat temu
rodzic
commit
b43dfeaa95

+ 99 - 35
bottle/app.py

@@ -1,12 +1,17 @@
-from bottle import Bottle, route, request, run, template
+from bottle import Bottle, route, request, run, template, response
 from bottle.ext import sqlalchemy 
 from sqlalchemy import create_engine, Column, Integer, Unicode
 from sqlalchemy.ext.declarative import declarative_base
 from random import randint
-import ujson
+import sys
 from operator import attrgetter, itemgetter
 from functools import partial
 
+try:
+    import ujson as json
+except ImportError:
+    import json
+
 app = Bottle()
 app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://benchmarkdbuser:benchmarkdbpass@DBHOSTNAME:3306/hello_world?charset=utf8'
 Base = declarative_base()
@@ -14,6 +19,9 @@ db_engine = create_engine(app.config['SQLALCHEMY_DATABASE_URI'])
 plugin = sqlalchemy.Plugin(db_engine, keyword='db', )
 app.install(plugin)
 
+if sys.version_info[0] == 3:
+    xrange = range
+
 
 class World(Base):
   __tablename__ = "World"
@@ -34,46 +42,52 @@ class Fortune(Base):
   id = Column(Integer, primary_key=True)
   message = Column(Unicode)
 
+
 @app.route("/json")
 def hello():
-  resp = {"message": "Hello, World!"}
-  return ujson.dumps(resp)
+    response.content_type = 'application/json'
+    resp = {"message": "Hello, World!"}
+    return json.dumps(resp)
 
 @app.route("/db")
 def get_random_world(db):
-  num_queries = request.query.queries or '1'
-  worlds = []
-  rp = partial(randint, 1, 10000)
-  for i in xrange(int(num_queries)):
-    worlds.append(db.query(World).get(rp()).serialize)
-  return ujson.dumps(worlds)
+    num_queries = request.query.get('queries', 1, type=int)
+    worlds = []
+    rp = partial(randint, 1, 10000)
+    for i in xrange(num_queries):
+        worlds.append(db.query(World).get(rp()).serialize)
+    response.content_type = 'application/json'
+    return json.dumps(worlds)
 
 @app.route("/dbs")
 def get_random_world_single(db):
-  wid = randint(1, 10000)
-  worlds = [db.query(World).get(wid).serialize]
-  return ujson.dumps(worlds)
+    wid = randint(1, 10000)
+    worlds = [db.query(World).get(wid).serialize]
+    response.content_type = 'application/json'
+    return json.dumps(worlds)
   
 @app.route("/dbraw")
 def get_random_world_raw():
-  connection = db_engine.connect()
-  num_queries = request.query.queries or '1'
-  worlds = []
-  rp = partial(randint, 1, 10000)
-  for i in range(int(num_queries)):
-    result = connection.execute("SELECT * FROM world WHERE id = " + str(rp())).fetchone()
-    worlds.append({'id': result[0], 'randomNumber': result[1]})
-  connection.close()
-  return ujson.dumps(worlds)
+    connection = db_engine.connect()
+    num_queries = request.query.get('queries', 1, type=int)
+    worlds = []
+    rp = partial(randint, 1, 10000)
+    for i in xrange(int(num_queries)):
+        result = connection.execute("SELECT * FROM world WHERE id = " + str(rp())).fetchone()
+        worlds.append({'id': result[0], 'randomNumber': result[1]})
+    connection.close()
+    response.content_type = 'application/json'
+    return json.dumps(worlds)
 
 @app.route("/dbsraw")
 def get_random_world_single_raw():
-  connection = db_engine.connect()
-  wid = randint(1, 10000)
-  result = connection.execute("SELECT * FROM world WHERE id = " + str(wid)).fetchone()
-  worlds = [{'id': result[0], 'randomNumber': result[1]}]
-  connection.close()
-  return ujson.dumps(worlds)
+    connection = db_engine.connect()
+    wid = randint(1, 10000)
+    result = connection.execute("SELECT * FROM world WHERE id = " + str(wid)).fetchone()
+    worlds = [{'id': result[0], 'randomNumber': result[1]}]
+    connection.close()
+    response.content_type = 'application/json'
+    return json.dumps(worlds)
 
 @app.route("/fortune")
 def fortune_orm(db):
@@ -84,12 +98,62 @@ def fortune_orm(db):
 
 @app.route("/fortuneraw")
 def fortune_raw():
-  connection = db_engine.connect()
-  fortunes=[(f.id, f.message) for f in connection.execute("SELECT * FROM Fortune")]
-  fortunes.append((0L, u'Additional fortune added at request time.'))
-  fortunes=sorted(fortunes, key=itemgetter(1))
-  connection.close()
-  return template('fortune', fortunes=fortunes)
+    connection = db_engine.connect()
+    fortunes=[(f.id, f.message) for f in connection.execute("SELECT * FROM Fortune")]
+    fortunes.append((0, u'Additional fortune added at request time.'))
+    fortunes=sorted(fortunes, key=itemgetter(1))
+    connection.close()
+    return template('fortune', fortunes=fortunes)
+
+
[email protected]("/updates")
+def updates(db):
+    """Test 5: Database Updates"""
+    num_queries = request.query.get('queries', 1, type=int)
+    if num_queries > 500:
+        num_queries = 500
+
+    worlds = []
+    rp = partial(randint, 1, 10000)
+    ids = [rp() for _ in xrange(num_queries)]
+    ids.sort()  # To avoid deadlock
+    for id in ids:
+        world = db.query(World).get(id)
+        world.randomNumber = rp()
+        worlds.append(world.serialize)
+
+    response.content_type = 'application/json'
+    return json.dumps(worlds)
+
+
[email protected]("/raw-updates")
+def raw_updates():
+    """Test 5: Database Updates"""
+    num_queries = request.query.get('queries', 1, type=int)
+    if num_queries > 500:
+        num_queries = 500
+
+    conn = db_engine.connect()
+
+    worlds = []
+    rp = partial(randint, 1, 10000)
+    for i in xrange(num_queries):
+        world = conn.execute("SELECT * FROM World WHERE id=%s", (rp(),)).fetchone()
+        randomNumber = rp()
+        worlds.append({'id': world['id'], 'randomNumber': randomNumber})
+        conn.execute("UPDATE World SET randomNumber=%s WHERE id=%s",
+                     (randomNumber, world['id']))
+    conn.close()
+    response.content_type = 'application/json'
+    return json.dumps(worlds)
+
+
[email protected]('/plaintext')
+def plaintext():
+    """Test 6: Plaintext"""
+    response.content_type = 'text/plain'
+    return b'Hello, World!'
+
 
 if __name__ == "__main__":
-    app.run(host='0.0.0.0', debug=True)
+    app.run(host='0.0.0.0', debug=False)

+ 25 - 0
bottle/benchmark_config

@@ -7,14 +7,39 @@
       "db_url": "/dbs",
       "query_url": "/db?queries=",
       "fortune_url": "/fortune",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "sort": 88
     },
+    "py3": {
+      "setup_file": "setup_py3",
+      "json_url": "/json",
+      "db_url": "/dbs",
+      "query_url": "/db?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "sort": 239
+    },
+    "pypy": {
+      "setup_file": "setup_pypy",
+      "json_url": "/json",
+      "db_url": "/dbs",
+      "query_url": "/db?queries=",
+      "fortune_url": "/fortune",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "sort": 240
+    },
     "mysql-raw": {
       "setup_file": "setup",
       "db_url": "/dbsraw",
       "query_url": "/dbraw?queries=",
       "fortune_url": "/fortuneraw",
+      "update_url": "/raw-updates?queries=",
       "port": 8080,
       "sort": 89
     }

+ 25 - 16
bottle/setup.py

@@ -1,23 +1,32 @@
 import subprocess
-import sys
 import setup_util
+import multiprocessing
 import os
 
+bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
+NCPU = multiprocessing.cpu_count()
+
+proc = None
+
+
 def start(args):
-  setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
-  subprocess.Popen("gunicorn app:app --worker-class=\"egg:meinheld#gunicorn_worker\" -b 0.0.0.0:8080 -w " + str((args.max_threads * 2)) + " --preload --log-level=critical", shell=True, cwd="bottle")
-  
-  return 0
+    global proc
+    setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
+    proc = subprocess.Popen([
+        bin_dir + "/gunicorn",
+        "app:app",
+        "-k", "meinheld.gmeinheld.MeinheldWorker",
+        "-b", "0.0.0.0:8080",
+        '-w', str(NCPU*3),
+        "--log-level=critical"],
+        cwd="bottle")
+    return 0
 
 def stop():
-  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
-  out, err = p.communicate()
-  for line in out.splitlines():
-    if 'gunicorn' in line:
-      try:
-        pid = int(line.split(None, 2)[1])
-        os.kill(pid, 9)
-      except OSError:
-        pass
-  
-  return 0
+    global proc
+    if proc is None:
+        return 0
+    proc.terminate()
+    proc.wait()
+    proc = None
+    return 0

+ 32 - 0
bottle/setup_py3.py

@@ -0,0 +1,32 @@
+import subprocess
+import setup_util
+import multiprocessing
+import os
+
+bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py3/bin')
+NCPU = multiprocessing.cpu_count()
+
+proc = None
+
+
+def start(args):
+    global proc
+    setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
+    proc = subprocess.Popen([
+        bin_dir + "/gunicorn",
+        "app:app",
+        "-k", "meinheld.gmeinheld.MeinheldWorker",
+        "-b", "0.0.0.0:8080",
+        '-w', str(NCPU*3),
+        "--log-level=critical"],
+        cwd="bottle")
+    return 0
+
+def stop():
+    global proc
+    if proc is None:
+        return 0
+    proc.terminate()
+    proc.wait()
+    proc = None
+    return 0

+ 33 - 0
bottle/setup_pypy.py

@@ -0,0 +1,33 @@
+import subprocess
+import setup_util
+import multiprocessing
+import os
+
+bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/pypy/bin')
+NCPU = multiprocessing.cpu_count()
+
+proc = None
+
+
+def start(args):
+    global proc
+    setup_util.replace_text("bottle/app.py", "DBHOSTNAME", args.database_host)
+    proc = subprocess.Popen([
+        bin_dir + "/gunicorn",
+        "app:app",
+        "-k", "tornado",
+        "-b", "0.0.0.0:8080",
+        '-w', str(NCPU*3),
+        "--log-level=critical"],
+        cwd="bottle")
+    return 0
+
+def stop():
+    global proc
+    if proc is None:
+        return 0
+    proc.terminate()
+    proc.wait()
+    proc = None
+    return 0
+

+ 8 - 0
django-stripped/benchmark_config

@@ -8,6 +8,14 @@
       "query_url": "/db?queries=",
       "port": 8080,
       "sort": 33
+    },
+    "py3": {
+      "setup_file": "setup_py3",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/db?queries=",
+      "port": 8080,
+      "sort": 243
     }
   }]
 }

+ 28 - 17
django-stripped/setup.py

@@ -1,23 +1,34 @@
 import subprocess
-import multiprocessing
-import sys
 import setup_util
+import multiprocessing
 import os
 
+home = os.path.expanduser('~')
+bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
+NCPU = multiprocessing.cpu_count()
+
+proc = None
+
+
 def start(args):
-  setup_util.replace_text("django-stripped/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
-  subprocess.Popen("gunicorn hello.wsgi:application --worker-class=\"egg:meinheld#gunicorn_worker\" -b 0.0.0.0:8080 -w " +
-                   str((multiprocessing.cpu_count() * 3)) + " --log-level=critical", shell=True, cwd="django-stripped/hello")
-  return 0
-def stop():
-  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
-  out, err = p.communicate()
-  for line in out.splitlines():
-    if 'gunicorn' in line:
-      try:
-        pid = int(line.split(None, 2)[1])
-        os.kill(pid, 9)
-      except OSError:
-        pass
+    global proc
+    setup_util.replace_text("django-stripped/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
+    setup_util.replace_text("django-stripped/hello/hello/settings.py", "\/home\/ubuntu",  home)
+    proc = subprocess.Popen([
+        bin_dir + "/gunicorn",
+        "hello.wsgi:application",
+        "-k", "meinheld.gmeinheld.MeinheldWorker",
+        "-b", "0.0.0.0:8080",
+        '-w', str(NCPU*3),
+        "--log-level=critical"],
+        cwd="django-stripped/hello")
+    return 0
 
-  return 0
+def stop():
+    global proc
+    if proc is None:
+        return 0
+    proc.terminate()
+    proc.wait()
+    proc = None
+    return 0

+ 34 - 0
django-stripped/setup_py3.py

@@ -0,0 +1,34 @@
+import subprocess
+import setup_util
+import multiprocessing
+import os
+
+home = os.path.expanduser('~')
+bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py3/bin')
+NCPU = multiprocessing.cpu_count()
+
+proc = None
+
+
+def start(args):
+    global proc
+    setup_util.replace_text("django-stripped/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
+    setup_util.replace_text("django-stripped/hello/hello/settings.py", "\/home\/ubuntu",  home)
+    proc = subprocess.Popen([
+        bin_dir + "/gunicorn",
+        "hello.wsgi:application",
+        "-k", "meinheld.gmeinheld.MeinheldWorker",
+        "-b", "0.0.0.0:8080",
+        '-w', str(NCPU*3),
+        "--log-level=critical"],
+        cwd="django-stripped/hello")
+    return 0
+
+def stop():
+    global proc
+    if proc is None:
+        return 0
+    proc.terminate()
+    proc.wait()
+    proc = None
+    return 0

+ 10 - 0
django/benchmark_config

@@ -10,6 +10,16 @@
       "update_url": "/update?queries=",
       "port": 8080,
       "sort": 3
+    },
+    "py3": {
+      "setup_file": "setup_py3",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/db?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/update?queries=",
+      "port": 8080,
+      "sort": 242
     }
   }]
 }

+ 6 - 6
django/hello/world/views.py

@@ -7,10 +7,13 @@ from world.models import World, Fortune
 from django.shortcuts import render
 from ujson import dumps as uj_dumps
 import random
+import sys
 from operator import attrgetter
-import numpy.random as nprnd
 from functools import partial
 
+if sys.version_info[0] == 3:
+  xrange = range
+
 def json(request):
   response = {
     "message": "Hello, World!"
@@ -31,12 +34,9 @@ def db(request):
   # one can eliminate dereferences by storing the end dereferenced thing in an identifier
   g = World.objects.get
   #r = random.randint
-  # but wait! there's more!
-  #http://stackoverflow.com/questions/4172131/create-random-list-of-integers-in-python
-  #r = nprnd.randint
   # but wait!  there's more!  if we're calling a function over and over with the same parameters, 
   # we can use even more function magic.
-  rp = partial(nprnd.randint, 1, 10000)
+  rp = partial(random.randint, 1, 10000)
   # now we're ready to write our awesome query iterator thingy
   # first of all, we know the id's correspond to the random number we're picking, so we can create
   # dictionaries on the fly instead of serializing later
@@ -58,7 +58,7 @@ def fortunes(request):
 def update(request):
   queries = int(request.GET.get('queries', 1))
   g = World.objects.get
-  rp = partial(nprnd.randint, 1, 10000)
+  rp = partial(random.randint, 1, 10000)
   
   worlds = []
   for r in [rp() for q in xrange(queries)]:

+ 27 - 20
django/setup.py

@@ -1,27 +1,34 @@
 import subprocess
-import multiprocessing
-import sys
 import setup_util
+import multiprocessing
 import os
-from os.path import expanduser
 
-home = expanduser("~")
+home = os.path.expanduser('~')
+bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
+NCPU = multiprocessing.cpu_count()
+
+proc = None
+
 
 def start(args):
-  setup_util.replace_text("django/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
-  setup_util.replace_text("django/hello/hello/settings.py", "\/home\/ubuntu",  home)
-  subprocess.Popen("gunicorn hello.wsgi:application --worker-class=\"egg:meinheld#gunicorn_worker\"  -b 0.0.0.0:8080 -w " +
-                   str((multiprocessing.cpu_count() * 3)) + " --log-level=critical", shell=True, cwd="django/hello")
-  return 0
-def stop():
-  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
-  out, err = p.communicate()
-  for line in out.splitlines():
-    if 'gunicorn' in line:
-      try:
-        pid = int(line.split(None, 2)[1])
-        os.kill(pid, 9)
-      except OSError:
-        pass
+    global proc
+    setup_util.replace_text("django/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
+    setup_util.replace_text("django/hello/hello/settings.py", "\/home\/ubuntu",  home)
+    proc = subprocess.Popen([
+        bin_dir + "/gunicorn",
+        "hello.wsgi:application",
+        "-k", "meinheld.gmeinheld.MeinheldWorker",
+        "-b", "0.0.0.0:8080",
+        '-w', str(NCPU*3),
+        "--log-level=critical"],
+        cwd="django/hello")
+    return 0
 
-  return 0
+def stop():
+    global proc
+    if proc is None:
+        return 0
+    proc.terminate()
+    proc.wait()
+    proc = None
+    return 0

+ 34 - 0
django/setup_py3.py

@@ -0,0 +1,34 @@
+import subprocess
+import setup_util
+import multiprocessing
+import os
+
+home = os.path.expanduser('~')
+bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py3/bin')
+NCPU = multiprocessing.cpu_count()
+
+proc = None
+
+
+def start(args):
+    global proc
+    setup_util.replace_text("django/hello/hello/settings.py", "HOST': '.*'", "HOST': '" + args.database_host + "'")
+    setup_util.replace_text("django/hello/hello/settings.py", "\/home\/ubuntu",  home)
+    proc = subprocess.Popen([
+        bin_dir + "/gunicorn",
+        "hello.wsgi:application",
+        "-k", "meinheld.gmeinheld.MeinheldWorker",
+        "-b", "0.0.0.0:8080",
+        '-w', str(NCPU*3),
+        "--log-level=critical"],
+        cwd="django/hello")
+    return 0
+
+def stop():
+    global proc
+    if proc is None:
+        return 0
+    proc.terminate()
+    proc.wait()
+    proc = None
+    return 0

+ 84 - 29
flask/app.py

@@ -1,9 +1,16 @@
 #!/usr/bin/env python
+from functools import partial
+from operator import attrgetter
+from random import randint
+import sys
+
+import flask
 from flask import Flask, jsonify, request, render_template
 from flask.ext.sqlalchemy import SQLAlchemy
 from sqlalchemy import create_engine
-from random import randint
-from operator import attrgetter
+
+if sys.version_info[0] == 3:
+    xrange = range
 
 try:
     import MySQLdb
@@ -15,6 +22,7 @@ except ImportError:
 
 app = Flask(__name__)
 app.config['SQLALCHEMY_DATABASE_URI'] = mysql_schema + '//benchmarkdbuser:benchmarkdbpass@DBHOSTNAME:3306/hello_world?charset=utf8'
+app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False
 db = SQLAlchemy(app)
 dbraw_engine = create_engine(app.config['SQLALCHEMY_DATABASE_URI'])
 
@@ -44,44 +52,45 @@ class Fortune(db.Model):
 
 @app.route("/json")
 def hello():
-  resp = {"message": "Hello, World!"}
-  return jsonify(resp)
+    return jsonify(message='Hello, World!')
+
 
 @app.route("/db")
 def get_random_world():
-  num_queries = request.args.get("queries", 1)
-  worlds = []
-  for i in range(int(num_queries)):
-    wid = randint(1, 10000)
-    worlds.append(World.query.get(wid).serialize)
-  return jsonify(worlds=worlds)
+    num_queries = request.args.get("queries", 1, type=int)
+    worlds = [World.query.get(randint(1, 10000)).serialize
+              for _ in xrange(num_queries)]
+    return jsonify(worlds=worlds)
+
 
 @app.route("/dbs")
 def get_random_world_single():
-  wid = randint(1, 10000)
-  worlds = [World.query.get(wid).serialize]
-  return jsonify(worlds=worlds)
-  
+    wid = randint(1, 10000)
+    worlds = [World.query.get(wid).serialize]
+    return jsonify(worlds=worlds)
+
+
 @app.route("/dbraw")
 def get_random_world_raw():
-  connection = dbraw_engine.connect()
-  num_queries = request.args.get("queries", 1)
-  worlds = []
-  for i in range(int(num_queries)):
-    wid = randint(1, 10000)
-    result = connection.execute("SELECT * FROM world WHERE id = " + str(wid)).fetchone()
-    worlds.append({'id': result[0], 'randomNumber': result[1]})
-  connection.close()
-  return jsonify(worlds=worlds)
+    connection = dbraw_engine.connect()
+    num_queries = request.args.get("queries", 1, type=int)
+    worlds = []
+    for i in xrange(num_queries):
+        wid = randint(1, 10000)
+        result = connection.execute("SELECT * FROM World WHERE id = " + str(wid)).fetchone()
+        worlds.append({'id': result[0], 'randomNumber': result[1]})
+    connection.close()
+    return jsonify(worlds=worlds)
+
 
 @app.route("/dbsraw")
 def get_random_world_single_raw():
-  connection = dbraw_engine.connect()
-  wid = randint(1, 10000)
-  result = connection.execute("SELECT * FROM world WHERE id = " + str(wid)).fetchone()
-  worlds = [{'id': result[0], 'randomNumber': result[1]}]
-  connection.close()
-  return jsonify(worlds=worlds)
+    connection = dbraw_engine.connect()
+    wid = randint(1, 10000)
+    result = connection.execute("SELECT * FROM World WHERE id = " + str(wid)).fetchone()
+    worlds = [{'id': result[0], 'randomNumber': result[1]}]
+    connection.close()
+    return jsonify(worlds=worlds)
 
 @app.route("/fortunes")
 def get_fortunes():
@@ -97,6 +106,52 @@ def get_forutens_raw():
     fortunes.sort(key=attrgetter('message'))
     return render_template('fortunes.html', fortunes=fortunes)
 
+
[email protected]("/updates")
+def updates():
+    """Test 5: Database Updates"""
+    num_queries = request.args.get('queries', 1, type=int)
+    if num_queries > 500:
+        num_queries = 500
+
+    worlds = []
+    rp = partial(randint, 1, 10000)
+    ids = [rp() for _ in xrange(num_queries)]
+    ids.sort()  # To avoid deadlock
+    for id in ids:
+        world = World.query.get(id)
+        world.randomNumber = rp()
+        worlds.append(world.serialize)
+    db.session.commit()
+    return jsonify(worlds)
+
+
[email protected]("/raw-updates")
+def raw_updates():
+    """Test 5: Database Updates"""
+    num_queries = request.args.get('queries', 1, type=int)
+    if num_queries > 500:
+        num_queries = 500
+
+    worlds = []
+    rp = partial(randint, 1, 10000)
+    for i in xrange(num_queries):
+        world = dbraw_engine.execute("SELECT * FROM World WHERE id=%s", (rp(),)).fetchone()
+        randomNumber = rp()
+        worlds.append({'id': world['id'], 'randomNumber': randomNumber})
+        dbraw_engine.execute("UPDATE World SET randomNumber=%s WHERE id=%s",
+                             (randomNumber, world['id']))
+    return jsonify(worlds=worlds)
+
+
[email protected]('/plaintext')
+def plaintext():
+    """Test 6: Plaintext"""
+    response = flask.make_response(b'Hello, World!')
+    response.content_type = 'text/plain'
+    return response
+
+
 # entry point for debugging
 if __name__ == "__main__":
     app.run(debug=True)

+ 17 - 0
flask/benchmark_config

@@ -7,6 +7,8 @@
       "db_url": "/dbs",
       "query_url": "/db?queries=",
       "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "sort": 31
     },
@@ -15,15 +17,29 @@
       "db_url": "/dbsraw",
       "query_url": "/dbraw?queries=",
       "fortune_url": "/fortunesraw",
+      "update_url": "/raw-updates?queries=",
       "port": 8080,
       "sort": 84
     },
+    "py3": {
+      "setup_file": "setup_py3",
+      "json_url": "/json",
+      "db_url": "/dbs",
+      "query_url": "/db?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "sort": 241
+    },
     "pypy": {
       "setup_file": "setup_pypy",
       "json_url": "/json",
       "db_url": "/dbs",
       "query_url": "/db?queries=",
       "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "sort": 110
     },
@@ -32,6 +48,7 @@
       "db_url": "/dbsraw",
       "query_url": "/dbraw?queries=",
       "fortune_url": "/fortunesraw",
+      "update_url": "/raw-updates?queries=",
       "port": 8080,
       "sort": 111
     }

+ 0 - 14
flask/run_pypy.py

@@ -1,14 +0,0 @@
-import tornado.options
-import tornado.wsgi
-import tornado.httpserver
-from tornado.options import options
-
-tornado.options.define('port', default=8080, type=int, help=("Server port"))
-tornado.options.parse_command_line()
-
-import app
-container = tornado.wsgi.WSGIContainer(app.app)
-server = tornado.httpserver.HTTPServer(container)
-server.bind(options.port)
-server.start(8)
-tornado.ioloop.IOLoop.instance().start()

+ 24 - 16
flask/setup.py

@@ -1,23 +1,31 @@
 import subprocess
-import sys
 import setup_util
+import multiprocessing
 import os
 
+bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
+NCPU = multiprocessing.cpu_count()
+
+proc = None
+
+
 def start(args):
-  setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
-  subprocess.Popen("gunicorn app:app --worker-class=\"egg:meinheld#gunicorn_worker\" -b 0.0.0.0:8080 -w " + str((args.max_threads * 2)) + " --preload --log-level=critical", shell=True, cwd="flask")
-  
-  return 0
+    global proc
+    setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
+    proc = subprocess.Popen([
+        bin_dir + "/gunicorn",
+        "app:app",
+        "-k", "meinheld.gmeinheld.MeinheldWorker",
+        "-b", "0.0.0.0:8080",
+        '-w', str(NCPU*3),
+        "--log-level=critical"],
+        cwd="flask")
+    return 0
 
 def stop():
-  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
-  out, err = p.communicate()
-  for line in out.splitlines():
-    if 'gunicorn' in line:
-      try:
-        pid = int(line.split(None, 2)[1])
-        os.kill(pid, 9)
-      except OSError:
-        pass
-  
-  return 0
+    global proc
+    if proc is None:
+        return 0
+    proc.terminate()
+    proc = None
+    return 0

+ 31 - 0
flask/setup_py3.py

@@ -0,0 +1,31 @@
+import subprocess
+import setup_util
+import multiprocessing
+import os
+
+bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py3/bin')
+NCPU = multiprocessing.cpu_count()
+
+proc = None
+
+
+def start(args):
+    global proc
+    setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
+    proc = subprocess.Popen([
+        bin_dir + "/gunicorn",
+        "app:app",
+        "-k", "meinheld.gmeinheld.MeinheldWorker",
+        "-b", "0.0.0.0:8080",
+        '-w', str(NCPU*3),
+        "--log-level=critical"],
+        cwd="flask")
+    return 0
+
+def stop():
+    global proc
+    if proc is None:
+        return 0
+    proc.terminate()
+    proc = None
+    return 0

+ 22 - 15
flask/setup_pypy.py

@@ -1,24 +1,31 @@
 import subprocess
-import sys
 import setup_util
+import multiprocessing
 import os
 
+bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/pypy/bin')
+NCPU = multiprocessing.cpu_count()
+
 proc = None
 
+
 def start(args):
-  global proc
-  setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
-  proc = subprocess.Popen("~/FrameworkBenchmarks/installs/pypy-2.0.2/bin/pypy run_pypy.py --port=8080 --logging=error", shell=True, cwd="flask")
-  return 0
+    global proc
+    setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
+    proc = subprocess.Popen([
+        bin_dir + "/gunicorn",
+        "app:app",
+        '-k', 'tornado',
+        "-b", "0.0.0.0:8080",
+        '-w', str(NCPU*3),
+        "--log-level=critical"],
+        cwd="flask")
+    return 0
 
 def stop():
-  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
-  out, err = p.communicate()
-  for line in out.splitlines():
-    if 'pypy' in line and 'run-tests' not in line:
-      try:
-        pid = int(line.split(None, 2)[1])
-        os.kill(pid, 9)
-      except OSError:
-        pass
-  return 0 
+    global proc
+    if proc is None:
+        return 0
+    proc.terminate()
+    proc = None
+    return 0

+ 69 - 63
installer.py

@@ -38,6 +38,8 @@ class Installer:
     # Languages
     #######################################
 
+    self._install_python()
+
     #
     # Dart
     #
@@ -51,37 +53,6 @@ class Installer:
     self.__run_command("sudo apt-get update")
     self.__run_command("sudo apt-get install esl-erlang", True)
 
-    #
-    # Python
-    #
-    
-    # .profile is not loaded yet. So we should use full path.
-    pypy_bin   = "~/FrameworkBenchmarks/installs/pypy-2.0.2/bin"
-    python_bin = "~/FrameworkBenchmarks/installs/python-2.7.5/bin"
-
-    self.__run_command("curl -L http://bitbucket.org/pypy/pypy/downloads/pypy-2.0.2-linux64.tar.bz2 | tar xj")
-    self.__run_command("curl http://www.python.org/ftp/python/2.7.5/Python-2.7.5.tgz | tar xz")
-    self.__run_command("./configure --prefix=$HOME/FrameworkBenchmarks/installs/python-2.7.5 --disable-shared", cwd="Python-2.7.5")
-    self.__run_command("make -j", cwd="Python-2.7.5")
-    self.__run_command("make install", cwd="Python-2.7.5")
-
-    self.__run_command("curl -L https://bitbucket.org/pypa/setuptools/downloads/setuptools-0.7.1.tar.gz | tar xz")
-    self.__run_command(pypy_bin + "/pypy setup.py install", cwd="setuptools-0.7.1")
-    self.__run_command(python_bin + "/python setup.py install", cwd="setuptools-0.7.1")
-
-    self.__run_command("curl https://pypi.python.org/packages/source/p/pip/pip-1.3.1.tar.gz | tar xz")
-    self.__run_command(pypy_bin + "/pypy setup.py install", cwd="pip-1.3.1")
-    self.__run_command(python_bin + "/python setup.py install", cwd="pip-1.3.1")
-
-    self.__run_command(python_bin + "/pip install MySQL-python==1.2.4")
-    self.__run_command(python_bin + "/pip install simplejson==3.3.0")
-    self.__run_command("curl http://initd.org/psycopg/tarballs/PSYCOPG-2-5/psycopg2-2.5.tar.gz | tar xz")
-    self.__run_command(python_bin + "/python setup.py install", cwd="psycopg2-2.5")
-    self.__run_command(python_bin + "/pip install --upgrade numpy==1.7.1")
-    self.__run_command(pypy_bin + "/pip install PyMySQL==0.5")
-    self.__run_command(python_bin + "/pip install PyMySQL==0.5")
-    self.__run_command(python_bin + "/easy_install -U 'ujson==1.30'")
-
     #
     # nodejs
     #
@@ -223,13 +194,6 @@ class Installer:
     self.__run_command("make", cwd="ngx_openresty-1.2.7.5")
     self.__run_command("sudo make install", cwd="ngx_openresty-1.2.7.5")
 
-    #
-    # Gunicorn
-    #
-
-    self.__run_command(python_bin + "/easy_install -U 'gunicorn==0.17.4'")
-    self.__run_command(python_bin + "/easy_install -U meinheld")
-
     #
     # Resin
     #
@@ -250,18 +214,6 @@ class Installer:
     #
     ##############################################################
 
-    ##############################
-    # Tornado
-    ##############################
-    packages = "tornado==3.0.1 motor==0.1 pymongo==2.5"
-    self.__run_command(python_bin + "/pip install " + packages)
-    self.__run_command(pypy_bin   + "/pip install " + packages)
-
-    ##############################
-    # Django
-    ##############################
-    self.__run_command(python_bin + "/pip install -U https://www.djangoproject.com/download/1.6a1/tarball/")
-
     ##############################
     # Grails
     ##############################
@@ -269,19 +221,6 @@ class Installer:
     self.__run_command("unzip -o grails-2.1.1.zip")
     self.__run_command("rm grails-2.1.1.zip")
 
-
-    ##############################
-    # Flask
-    ##############################
-    packages = "flask==0.9 flask-sqlalchemy==0.16 sqlalchemy==0.8.1 jinja2==2.7 werkzeug==0.8.3"
-    self.__run_command(python_bin + "/pip install " + packages)
-    self.__run_command(pypy_bin + "/pip install " + packages)
-
-    ##############################
-    # Bottle
-    ##############################
-    self.__run_command(python_bin + "/pip install bottle bottle-sqlalchemy")
-
     ##############################
     # Play 2
     ##############################
@@ -352,6 +291,72 @@ class Installer:
   # End __install_server_software
   ############################################################
 
+  def _install_python(self):
+    # .profile is not loaded yet. So we should use full path.
+    pypy_bin   = "~/FrameworkBenchmarks/installs/pypy/bin"
+    python_bin = "~/FrameworkBenchmarks/installs/py2/bin"
+    python3_bin= "~/FrameworkBenchmarks/installs/py3/bin"
+    def easy_install(pkg, two=True, three=False, pypy=False):
+      cmd = "/easy_install -ZU '" + pkg + "'"
+      if two:   self.__run_command(python_bin + cmd)
+      if three: self.__run_command(python3_bin + cmd)
+      if pypy:  self.__run_command(pypy_bin + cmd)
+
+    self.__run_command("curl -L http://bitbucket.org/pypy/pypy/downloads/pypy-2.0.2-linux64.tar.bz2 | tar xj")
+    self.__run_command('ln -s pypy-2.0.2 pypy')
+    self.__run_command("curl -L http://www.python.org/ftp/python/2.7.5/Python-2.7.5.tgz | tar xz")
+    self.__run_command("curl -L http://www.python.org/ftp/python/3.3.2/Python-3.3.2.tar.xz | tar xJ")
+    self.__run_command("./configure --prefix=$HOME/FrameworkBenchmarks/installs/py2 --disable-shared CC=gcc-4.8", cwd="Python-2.7.5")
+    self.__run_command("./configure --prefix=$HOME/FrameworkBenchmarks/installs/py3 --disable-shared CC=gcc-4.8", cwd="Python-3.3.2")
+    self.__run_command("make -j", cwd="Python-2.7.5")
+    self.__run_command("make install", cwd="Python-2.7.5")
+    self.__run_command("make -j", cwd="Python-3.3.2")
+    self.__run_command("make install", cwd="Python-3.3.2")
+
+
+    self.__run_command("wget https://bitbucket.org/pypa/setuptools/downloads/ez_setup.py")
+    self.__run_command(pypy_bin + "/pypy ez_setup.py")
+    self.__run_command(python_bin + "/python ez_setup.py")
+    self.__run_command(python3_bin + "/python3 ez_setup.py")
+
+    easy_install('pip==1.3.1', two=True, three=True, pypy=True)
+    easy_install('MySQL-python==1.2.4', two=True, three=False, pypy=True)
+    easy_install('https://github.com/clelland/MySQL-for-Python-3/archive/master.zip',
+                 two=False, three=True, pypy=False)
+    easy_install('PyMySQL==0.5', pypy=True)
+    easy_install('PyMySQL3==0.5', two=False, three=True)
+    easy_install('simplejson==3.3.0', two=True, three=True, pypy=False)
+    easy_install('psycopg2==2.5.1', three=True)
+    easy_install('ujson==1.33', three=True)
+    easy_install('https://github.com/downloads/surfly/gevent/gevent-1.0rc2.tar.gz', three=True)
+    easy_install('uwsgi', three=True)  # uwsgi is released too often to stick on single version.
+
+    # Gunicorn
+    easy_install('gunicorn==17.5', two=True, three=True, pypy=True)
+    # meinheld HEAD supports gunicorn worker on Python 3
+    easy_install('https://github.com/mopemope/meinheld/archive/master.zip',
+                 two=True, three=True, pypy=True)
+
+    # Tornado
+    easy_install('tornado==3.1', two=True, three=True, pypy=True)
+    easy_install('motor==0.1.1', two=True, three=True, pypy=True)
+    easy_install('pymongo==2.5.2', two=True, three=True, pypy=True)
+
+    # Django
+    easy_install("https://www.djangoproject.com/download/1.6b1/tarball/", two=True, three=True, pypy=True)
+
+    # Flask
+    easy_install('Werkzeug==0.9.2', two=True, three=True, pypy=True)
+    easy_install('flask==0.10.1', two=True, three=True, pypy=True)
+    easy_install('sqlalchemy==0.8.2', two=True, three=True, pypy=True)
+    easy_install('Jinja2==2.7', two=True, three=True, pypy=True)
+    easy_install('Flask-SQLAlchemy==1.0', two=True, three=True, pypy=True)
+
+    # Bottle
+    easy_install('bottle==0.11.6', two=True, three=True, pypy=True)
+    easy_install('bottle-sqlalchemy==0.4', two=True, three=True, pypy=True)
+
+
   ############################################################
   # __install_client_software
   ############################################################
@@ -477,3 +482,4 @@ class Installer:
   # End __init__
   ############################################################
 
+# vim: sw=2

+ 5 - 5
setup_util.py

@@ -2,8 +2,8 @@ import re
 
 # Replaces all text found using the regular expression to_replace with the supplied replacement.
 def replace_text(file, to_replace, replacement):
-  with open(file, "r") as conf:
-    contents = conf.read()
-  replaced_text = re.sub(to_replace, replacement, contents)
-  with open(file, "w") as f:
-    f.write(replaced_text)
+    with open(file, "r") as conf:
+        contents = conf.read()
+    replaced_text = re.sub(to_replace, replacement, contents)
+    with open(file, "w") as f:
+        f.write(replaced_text)

+ 2 - 0
tornado/benchmark_config

@@ -4,6 +4,7 @@
     "default": {
       "setup_file": "setup",
       "json_url": "/json",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "sort": 52
     },
@@ -19,6 +20,7 @@
       "json_url": "/json",
       "db_url": "/db",
       "query_url": "/db?queries=",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "sort": 134
     }

+ 14 - 10
tornado/server.py

@@ -1,29 +1,32 @@
+import random
+import sys
+
+import motor
 import tornado.ioloop
 import tornado.web
-from tornado import gen
-import motor
-import random
-from tornado import escape
+from tornado import gen, escape
 import tornado.options
 from tornado.options import options
 import tornado.httpserver
 
+tornado.options.define('port', default=8888, type=int, help="Server port")
 
-tornado.options.define('port', default=8888, type=int, help=(
-    "Server port"))
 
-class JsonSerializeTestHandler(tornado.web.RequestHandler):
+class BaseHandler(torando.web.RequestHandler):
     def compute_etag(self):
         return None
 
+class JsonSerializeTestHandler(BaseHandler):
     def get(self):
         obj = dict(message="Hello, World!")
         self.write(obj)
 
-class QueryTestHandler(tornado.web.RequestHandler):
-    def compute_etag(self):
-        return None
+class PlaintextHandler(BaseHandler):
+    def get(self):
+        self.set_header('Content-Type', 'text/plain')
+        self.write(b"Hello, World!")
 
+class QueryTestHandler(BaseHandler):
     @tornado.web.asynchronous
     @gen.coroutine
     def get(self):
@@ -49,6 +52,7 @@ class QueryTestHandler(tornado.web.RequestHandler):
 
 application = tornado.web.Application([
     (r"/json", JsonSerializeTestHandler),
+    (r"/plaintext", PlaintextHandler),
     (r"/db", QueryTestHandler),
 ])
 

+ 11 - 16
tornado/setup.py

@@ -1,30 +1,25 @@
 import subprocess
-import sys
 import setup_util
-import os
 from os.path import expanduser
 
-home = expanduser("~")
-cwd = "%s/FrameworkBenchmarks/tornado" % home
+python = expanduser('~/FrameworkBenchmarks/installs/py2/bin/python')
+cwd = expanduser('~/FrameworkBenchmarks/tornado')
+proc = None
 
 
 def start(args):
+    global proc
     setup_util.replace_text(
         cwd + "/server.py", "localhost", args.database_host)
 
-    subprocess.Popen("python %s/FrameworkBenchmarks/tornado/server.py --port=8080 --logging=error" % home, shell=True, cwd=cwd)
+    proc = subprocess.Popen(
+        python + " server.py --port=8080 --logging=error",
+        shell=True, cwd=cwd)
     return 0
 
-
 def stop():
-    p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
-    out, err = p.communicate()
-    for line in out.splitlines():
-        if 'server.py' in line:
-            #try:
-            pid = int(line.split(None, 2)[1])
-            os.kill(pid, 9)
-            #except OSError:
-            #    pass
-
+    global proc
+    if proc:
+        proc.terminate()
+        proc = None
     return 0

+ 25 - 0
tornado/setup_py3.py

@@ -0,0 +1,25 @@
+import subprocess
+import setup_util
+from os.path import expanduser
+
+python = expanduser('~/FrameworkBenchmarks/installs/py3/bin/python')
+cwd = expanduser('~/FrameworkBenchmarks/tornado')
+proc = None
+
+
+def start(args):
+    global proc
+    setup_util.replace_text(
+        cwd + "/server.py", "localhost", args.database_host)
+
+    proc = subprocess.Popen(
+        python + " server.py --port=8080 --logging=error",
+        shell=True, cwd=cwd)
+    return 0
+
+def stop():
+    global proc
+    if proc:
+        proc.terminate()
+        proc = None
+    return 0

+ 11 - 17
tornado/setup_pypy.py

@@ -1,31 +1,25 @@
 import subprocess
-import sys
 import setup_util
-import os
 from os.path import expanduser
 
-home = expanduser("~")
-cwd = "%s/FrameworkBenchmarks/tornado" % home
-pypy = "%s/FrameworkBenchmarks/installs/pypy-2.0.2/bin/pypy" % home
+python = expanduser('~/FrameworkBenchmarks/installs/pypy/bin/python')
+cwd = expanduser('~/FrameworkBenchmarks/tornado')
+proc = None
 
 
 def start(args):
+    global proc
     setup_util.replace_text(
         cwd + "/server.py", "localhost", args.database_host)
 
-    subprocess.Popen(pypy + " %s/FrameworkBenchmarks/tornado/server.py --port=8080 --logging=error" % home, shell=True, cwd=cwd)
+    proc = subprocess.Popen(
+        python + " server.py --port=8080 --logging=error",
+        shell=True, cwd=cwd)
     return 0
 
-
 def stop():
-    p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
-    out, err = p.communicate()
-    for line in out.splitlines():
-        if 'server.py' in line:
-            #try:
-            pid = int(line.split(None, 2)[1])
-            os.kill(pid, 9)
-            #except OSError:
-            #    pass
-
+    global proc
+    if proc:
+        proc.terminate()
+        proc = None
     return 0

+ 19 - 0
uwsgi/README.md

@@ -0,0 +1,19 @@
+# WSGI Benchmarking Test
+
+This is the WSGI portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+### JSON Encoding Test
+
+
+* [JSON test controller/view](hello.py)
+
+## Infrastructure Software Versions
+The tests were run with:
+* [Python 2.7.3](http://www.python.org/)
+* [uwsgi 1.9.14](https://uwsgi-docs.readthedocs.org/)
+
+
+## Test URLs
+### JSON Encoding Test
+
+http://localhost:8080/json

+ 0 - 0
uwsgi/__init__.py


+ 11 - 0
uwsgi/benchmark_config

@@ -0,0 +1,11 @@
+{
+  "framework": "uwsgi",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "port": 8080,
+      "sort": 244
+    }
+  }]
+}

+ 14 - 0
uwsgi/hello.py

@@ -0,0 +1,14 @@
+import ujson
+
+
+def application(environ, start_response):
+    response = {
+      "message": "Hello, World!"
+    }
+    data = ujson.dumps(response)
+    response_headers = [
+        ('Content-type', 'text/plain'),
+        ('Content-Length', str(len(data)))
+    ]
+    start_response('200 OK', response_headers)
+    return [data]

+ 5 - 0
uwsgi/requirements.txt

@@ -0,0 +1,5 @@
+greenlet
+cython
+-e git://github.com/surfly/gevent.git#egg=gevent
+uwsgi
+ujson

+ 26 - 0
uwsgi/setup.py

@@ -0,0 +1,26 @@
+import multiprocessing
+import subprocess
+import sys
+import setup_util
+import os
+import time
+
+uwsgi = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin/uwsgi')
+PROCS = multiprocessing.cpu_count()
+
+def start(args):
+    subprocess.Popen(
+        uwsgi + ' --master -L --gevent 1000 --http :8080 --http-keepalive ' +
+        '-p ' + str(PROCS) + ' -w hello --add-header "Connection: keep-alive" ' +
+        ' --pidfile /tmp/uwsgi.pid',
+        shell=True, cwd="uwsgi")
+    return 0
+
+
+def stop():
+    try:
+        subprocess.Popen(uwsgi + ' --stop /tmp/uwsgi.pid', shell=True, cwd="uwsgi")
+    except OSError:
+        pass
+    time.sleep(1)
+    return 0

+ 2 - 1
wsgi/benchmark_config

@@ -4,8 +4,9 @@
     "default": {
       "setup_file": "setup",
       "json_url": "/json",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "sort": 29
     }
   }]
-}
+}

+ 26 - 5
wsgi/hello.py

@@ -1,14 +1,35 @@
 import ujson
+import sys
 
+if sys.version_info[0] == 3:
+    def encode(obj):
+        return ujson.dumps(obj).encode('utf-8')
+else:
+    def encode(obj):
+        return ujson.dumps(obj)
 
-def app(environ, start_response):
-    response = {
-      "message": "Hello, World!"
-    }
-    data = ujson.dumps(response)
+def json(environ, start_response):
+    response = {"message": "Hello, World!"}
+    data = encode(response)
+    response_headers = [
+        ('Content-type', 'application/json'),
+        ('Content-Length', str(len(data)))
+    ]
+    start_response('200 OK', response_headers)
+    return [data]
+
+def plaintext(environ, start_response):
+    data = b"Hello, World!"
     response_headers = [
         ('Content-type', 'text/plain'),
         ('Content-Length', str(len(data)))
     ]
     start_response('200 OK', response_headers)
     return [data]
+
+def app(environ, start_response):
+    path = environ['PATH_INFO']
+    if path.startswith('/json'):
+        return json(environ, start_response)
+    else:
+        return plaintext(environ, start_response)

+ 25 - 16
wsgi/setup.py

@@ -1,22 +1,31 @@
-
 import subprocess
-import sys
 import setup_util
+import multiprocessing
 import os
 
+bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
+NCPU = multiprocessing.cpu_count()
+
+proc = None
+
+
 def start(args):
-  subprocess.Popen('gunicorn hello:app --worker-class="egg:meinheld#gunicorn_worker" -b 0.0.0.0:8080 -w '
-                   + str((args.max_threads * 2)) + " --log-level=critical", shell=True, cwd="wsgi")
-  return 0
-def stop():
-  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
-  out, err = p.communicate()
-  for line in out.splitlines():
-    if 'gunicorn' in line:
-      try:
-        pid = int(line.split(None, 2)[1])
-        os.kill(pid, 9)
-      except OSError:
-        pass
+    global proc
+    proc = subprocess.Popen([
+        bin_dir + "/gunicorn",
+        "hello:app",
+        "-k", "meinheld.gmeinheld.MeinheldWorker",
+        "-b", "0.0.0.0:8080",
+        '-w', str(NCPU),
+        "--log-level=critical"],
+        cwd="wsgi")
+    return 0
 
-  return 0
+def stop():
+    global proc
+    if proc is None:
+        return 0
+    proc.terminate()
+    proc.wait()
+    proc = None
+    return 0