Browse Source

Split requirements.txt per test.

* Update gunicorn, SQLAlchemy and Python 2.7.8
* Use pid instead of search and kill
* Move config/uwsgi.ini and config/nginx_uwsgi.conf to each test directory.
* Use apt-get -q
INADA Naoki 11 years ago
parent
commit
02f77d2605
82 changed files with 763 additions and 589 deletions
  1. 12 2
      bottle/app.py
  2. 1 3
      bottle/bash_profile.sh
  3. 1 0
      bottle/gunicorn_conf.py
  4. 8 1
      bottle/install.sh
  5. 1 3
      bottle/nginx.conf
  6. 6 0
      bottle/requirements-pypy.txt
  7. 7 0
      bottle/requirements.txt
  8. 3 2
      bottle/setup.py
  9. 7 8
      bottle/setup_nginxuwsgi.py
  10. 3 3
      bottle/setup_py3.py
  11. 3 2
      bottle/setup_pypy.py
  12. 0 0
      bottle/uwsgi.ini
  13. 0 37
      config/requirements-py3.txt
  14. 0 36
      config/requirements-pypy.txt
  15. 0 38
      config/requirements.txt
  16. 0 4
      config/uwsgi_stop.ini
  17. 14 0
      django/bash_profile.sh
  18. 7 7
      django/benchmark_config
  19. 21 0
      django/gunicorn_conf.py
  20. 2 2
      django/hello/hello/settings.py
  21. 1 0
      django/hello/hello/urls.py
  22. 24 17
      django/hello/world/views.py
  23. 6 0
      django/install.sh
  24. 8 0
      django/requirements.txt
  25. 7 29
      django/setup.py
  26. 7 29
      django/setup_pg.py
  27. 7 29
      django/setup_py3.py
  28. 14 0
      falcon/bash_profile.sh
  29. 2 0
      falcon/gunicorn_conf.py
  30. 7 0
      falcon/install.sh
  31. 3 0
      falcon/requirements-pypy.txt
  32. 4 0
      falcon/requirements.txt
  33. 7 21
      falcon/setup.py
  34. 6 16
      falcon/setup_py3.py
  35. 7 23
      falcon/setup_pypy.py
  36. 16 1
      flask/app.py
  37. 14 0
      flask/bash_profile.sh
  38. 20 0
      flask/gunicorn_conf.py
  39. 8 1
      flask/install.sh
  40. 48 0
      flask/nginx.conf
  41. 8 0
      flask/requirements-pypy.txt
  42. 9 1
      flask/requirements.txt
  43. 15 27
      flask/setup.py
  44. 10 12
      flask/setup_nginxuwsgi.py
  45. 14 27
      flask/setup_py3.py
  46. 22 14
      flask/setup_pypy.py
  47. 19 0
      flask/uwsgi.ini
  48. 14 0
      pyramid/bash_profile.sh
  49. 4 4
      pyramid/benchmark_config
  50. 2 1
      pyramid/frameworkbenchmarks/models.py
  51. 21 0
      pyramid/gunicorn_conf.py
  52. 7 1
      pyramid/install.sh
  53. 6 0
      pyramid/requirements.txt
  54. 0 34
      pyramid/setup_benchmark.py
  55. 8 26
      pyramid/setup_py2.py
  56. 8 26
      pyramid/setup_py3.py
  57. 4 4
      toolset/run-ci.py
  58. 4 4
      toolset/setup/linux/languages/pypy.sh
  59. 8 7
      toolset/setup/linux/languages/python2.sh
  60. 5 2
      toolset/setup/linux/languages/python3.sh
  61. 2 2
      tornado/bash_profile.sh
  62. 1 1
      tornado/benchmark_config
  63. 7 1
      tornado/install.sh
  64. 3 2
      tornado/requirements.txt
  65. 2 1
      tornado/setup.py
  66. 15 9
      tornado/setup_py3.py
  67. 14 0
      uwsgi/bash_profile.sh
  68. 5 0
      uwsgi/install.sh
  69. 48 0
      uwsgi/nginx.conf
  70. 2 2
      uwsgi/requirements.txt
  71. 4 8
      uwsgi/setup.py
  72. 10 6
      uwsgi/setup_nginx.py
  73. 19 0
      uwsgi/uwsgi.ini
  74. 14 0
      wsgi/bash_profile.sh
  75. 22 0
      wsgi/gunicorn_conf.py
  76. 0 8
      wsgi/hello.py
  77. 5 0
      wsgi/install.sh
  78. 48 0
      wsgi/nginx.conf
  79. 4 3
      wsgi/requirements.txt
  80. 8 33
      wsgi/setup.py
  81. 11 9
      wsgi/setup_nginxuwsgi.py
  82. 19 0
      wsgi/uwsgi.ini

+ 12 - 2
bottle/app.py

@@ -17,9 +17,11 @@ except ImportError:
 if sys.version_info[0] == 3:
     xrange = range
 
+_is_pypy = hasattr(sys, 'pypy_version_info')
 
-DBHOSTNAME = os.environ.get('DBHOSTNAME', 'localhost')
-DATABASE_URI = 'mysql://benchmarkdbuser:benchmarkdbpass@%s:3306/hello_world?charset=utf8' % DBHOSTNAME
+DBDRIVER = 'mysql+pymysql' if _is_pypy else 'mysql'
+DBHOSTNAME = os.environ.get('TFB_DATABASE_HOST', 'localhost')
+DATABASE_URI = '%s://benchmarkdbuser:benchmarkdbpass@%s:3306/hello_world?charset=utf8' % (DBDRIVER, DBHOSTNAME)
 
 app = Bottle()
 Base = declarative_base()
@@ -85,6 +87,8 @@ def get_random_world_single_raw():
 def get_random_world(db):
     """Test Type 3: Multiple database queries"""
     num_queries = request.query.get('queries', 1, type=int)
+    if num_queries < 1:
+        num_queries = 1
     if num_queries > 500:
         num_queries = 500
     rp = partial(randint, 1, 10000)
@@ -97,6 +101,8 @@ def get_random_world(db):
 @app.route("/raw-queries")
 def get_random_world_raw():
     num_queries = request.query.get('queries', 1, type=int)
+    if num_queries < 1:
+        num_queries = 1
     if num_queries > 500:
         num_queries = 500
     worlds = []
@@ -136,6 +142,8 @@ def fortune_raw():
 def updates(db):
     """Test 5: Database Updates"""
     num_queries = request.query.get('queries', 1, type=int)
+    if num_queries < 1:
+        num_queries = 1
     if num_queries > 500:
         num_queries = 500
 
@@ -156,6 +164,8 @@ def updates(db):
 def raw_updates():
     """Test 5: Database Updates"""
     num_queries = request.query.get('queries', 1, type=int)
+    if num_queries < 1:
+        num_queries = 1
     if num_queries > 500:
         num_queries = 500
 

+ 1 - 3
bottle/bash_profile.sh

@@ -1,4 +1,3 @@
-
 export PY2_ROOT=$IROOT/py2
 export PY2=$PY2_ROOT/bin/python
 export PY2_PIP=$PY2_ROOT/bin/pip
@@ -11,6 +10,5 @@ export PYPY_GUNICORN=$PYPY_ROOT/bin/gunicorn
 
 export PY3_ROOT=$IROOT/py3
 export PY3=$PY3_ROOT/bin/python
-export PY3_PIP=$PY3_ROOT/bin/pip
+export PY3_PIP=$PY3_ROOT/bin/pip3
 export PY3_GUNICORN=$PY3_ROOT/bin/gunicorn
-

+ 1 - 0
bottle/gunicorn_conf.py

@@ -7,6 +7,7 @@ _is_pypy = hasattr(sys, 'pypy_version_info')
 workers = multiprocessing.cpu_count() * 3
 bind = "0.0.0.0:8080"
 keepalive = 120
+errorlog = '-'
 
 
 if _is_pypy:

+ 8 - 1
bottle/install.sh

@@ -1,3 +1,10 @@
 #!/bin/bash
 
-fw_depends python2 python3 pypy
+mkdir -p ~/.pip_cache
+export PIP_DOWNLOAD_CACHE=$HOME/.pip_cache
+
+fw_depends python2 python3 pypy nginx
+
+$IROOT/py2/bin/pip install -r $TROOT/requirements.txt
+$IROOT/py3/bin/pip3 install -r $TROOT/requirements.txt
+$IROOT/pypy/bin/pip install -r $TROOT/requirements-pypy.txt

+ 1 - 3
config/nginx_uwsgi.conf → bottle/nginx.conf

@@ -1,9 +1,7 @@
 # This file is based on /usr/local/nginx/conf/nginx.conf.default.
 
 # One worker process per core
-worker_processes auto;
 error_log stderr error;
-pid /tmp/nginx.pid;
 
 events {
     # This needed to be increased because the nginx error log said so.
@@ -13,8 +11,8 @@ events {
 }
 
 http {
-    include       /usr/local/nginx/conf/mime.types;
     default_type  application/octet-stream;
+    client_body_temp_path      /tmp;
 
     # turn off request logging for performance
     access_log off;

+ 6 - 0
bottle/requirements-pypy.txt

@@ -0,0 +1,6 @@
+bottle==0.12.7
+bottle-sqlalchemy==0.4.1
+SQLAlchemy==0.9.7
+gunicorn==19.1
+PyMySQL==0.6.2
+tornado==3.2.2

+ 7 - 0
bottle/requirements.txt

@@ -0,0 +1,7 @@
+bottle==0.12.7
+bottle-sqlalchemy==0.4.1
+SQLAlchemy==0.9.4
+gunicorn==19.1
+meinheld==0.5.6
+mysqlclient==1.3.1
+uwsgi

+ 3 - 2
bottle/setup.py

@@ -7,7 +7,9 @@ proc = None
 
 def start(args, logfile, errfile):
     global proc
-    proc = subprocess.Popen( "$PY2_GUNICORN -c gunicorn_conf.py -e DBHOSTNAME=%s app:app" % args.database_host, cwd="bottle", shell=True, stderr=errfile, stdout=logfile)
+    proc = subprocess.Popen(
+            "exec $PY2_GUNICORN -c gunicorn_conf.py app:app",
+            cwd="bottle", shell=True, stderr=errfile, stdout=logfile)
     return 0
 
 
@@ -20,5 +22,4 @@ def stop(logfile, errfile):
     proc = None
 
     subprocess.call("sudo pkill gunicorn", shell=True, stderr=errfile, stdout=logfile)
-
     return 0

+ 7 - 8
bottle/setup_nginxuwsgi.py

@@ -3,10 +3,10 @@ import multiprocessing
 import os
 
 
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
-config_dir = os.path.expanduser('~/FrameworkBenchmarks/config')
+CWD = os.path.abspath(os.path.dirname(__file__))
+bin_dir = os.path.expandvars('$PY2_ROOT/bin')
 NCPU = multiprocessing.cpu_count()
-NGINX_COMMAND = 'sudo /usr/local/nginx/sbin/nginx -c ' + config_dir + '/nginx_uwsgi.conf'
+NGINX_COMMAND = 'sudo /usr/local/nginx/sbin/nginx -c ' + CWD + '/nginx.conf'
 
 
 def start(args, logfile, errfile):
@@ -17,10 +17,9 @@ def start(args, logfile, errfile):
 
         # Run in the background, but keep stdout/stderr for easy debugging
         subprocess.Popen(
-            "{0}/uwsgi --ini {1}/uwsgi.ini --processes {2} --env DBHOSTNAME={3} --wsgi app:app".format(
-                bin_dir, config_dir, NCPU*3, args.database_host),
-            shell=True, cwd='bottle', stderr=errfile, stdout=logfile)
-
+            "{0}/uwsgi --ini uwsgi.ini --processes {1} --wsgi app:app".format(
+                bin_dir, NCPU*3),
+            shell=True, cwd=CWD, stderr=errfile, stdout=logfile)
         return 0
     except subprocess.CalledProcessError:
         return 1
@@ -31,6 +30,6 @@ def stop(logfile, errfile):
         NGINX_COMMAND + ' -s stop',
         shell=True, stdout=logfile, stderr=errfile)
 
-    subprocess.call(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi_stop.ini',
+    subprocess.call(bin_dir + '/uwsgi --stop /tmp/uwsgi.pid',
                     shell=True, stderr=errfile, stdout=logfile)
     return 0

+ 3 - 3
bottle/setup_py3.py

@@ -1,14 +1,15 @@
 import subprocess
 import os
 
-BIN = os.path.expanduser('~/FrameworkBenchmarks/installs/py3/bin')
 
 proc = None
 
 
 def start(args, logfile, errfile):
     global proc
-    proc = subprocess.Popen( "$PY3_GUNICORN -c gunicorn_conf.py -e DBHOSTNAME=%s app:app" % args.database_host, cwd="bottle", shell=True, stderr=errfile, stdout=logfile)
+    proc = subprocess.Popen(
+            "exec $PY3_GUNICORN -c gunicorn_conf.py app:app",
+            cwd="bottle", shell=True, stderr=errfile, stdout=logfile)
     return 0
 
 
@@ -21,5 +22,4 @@ def stop(logfile, errfile):
     proc = None
 
     subprocess.call("sudo pkill gunicorn", shell=True, stderr=errfile, stdout=logfile)
-
     return 0

+ 3 - 2
bottle/setup_pypy.py

@@ -6,7 +6,9 @@ proc = None
 
 def start(args, logfile, errfile):
     global proc
-    proc = subprocess.Popen( "$PYPY_GUNICORN -c gunicorn_conf.py -e DBHOSTNAME=%s app:app" % args.database_host, cwd="bottle", shell=True, stderr=errfile, stdout=logfile)
+    proc = subprocess.Popen(
+        "exec $PYPY_GUNICORN -c gunicorn_conf.py --error-logfile - app:app",
+        cwd="bottle", shell=True, stderr=errfile, stdout=logfile)
     return 0
 
 
@@ -19,5 +21,4 @@ def stop(logfile, errfile):
     proc = None
 
     subprocess.call("sudo pkill gunicorn", shell=True, stderr=errfile, stdout=logfile)
-
     return 0

+ 0 - 0
config/uwsgi.ini → bottle/uwsgi.ini


+ 0 - 37
config/requirements-py3.txt

@@ -1,37 +0,0 @@
-mysqlclient==1.3.1
-PyMySQL==0.6.2
-psycopg2==2.5.3
-
-simplejson==3.5.2
-ujson==1.33
-# gevent==1.0.1 # gevent doesn't support Python 3 yet.
-# uwsgi is released too often to stick on single version.
-uwsgi
-
-gunicorn==19.0
-meinheld==0.5.6
-
-# Tornado
-tornado==3.2.2
-motor==0.3
-# pymongo is installed via motor dependency
-
-# Django
-Django==1.6.5
-
-# Flask
-Jinja2==2.7.3
-Werkzeug==0.9.6
-flask==0.10.1
-SQLAlchemy==0.9.4
-Flask-SQLAlchemy==1.0
-
-# Bottle
-bottle==0.12.7
-bottle-sqlalchemy==0.4.1
-
-# Falcon
-Cython==0.20.1
-falcon==0.1.8
-
-Momoko==1.1.3

+ 0 - 36
config/requirements-pypy.txt

@@ -1,36 +0,0 @@
-mysqlclient==1.3.1
-PyMySQL==0.6.2
-# TODO: Try psycopg2cffi
-#psycopg2==2.5.2
-
-#simplejson==3.4.1
-#ujson==1.33
-#gevent==1.0.1
-# uwsgi is released too often to stick on single version.
-uwsgi
-
-gunicorn==18.0
-meinheld==0.5.6
-
-# Tornado
-tornado==3.2.2
-motor==0.3
-# pymongo is installed via motor dependency
-
-# Django
-Django==1.6.5
-
-# Flask
-Jinja2==2.7.3
-Werkzeug==0.9.6
-flask==0.10.1
-SQLAlchemy==0.9.4
-Flask-SQLAlchemy==1.0
-
-# Bottle
-bottle==0.12.7
-bottle-sqlalchemy==0.4.1
-
-# Falcon
-# Cython==0.20.1
-falcon==0.1.8

+ 0 - 38
config/requirements.txt

@@ -1,38 +0,0 @@
-circus
-mysqlclient==1.3.1
-PyMySQL==0.6.2
-psycopg2==2.5.3
-
-simplejson==3.5.2
-ujson==1.33
-gevent==1.0.1
-# uwsgi is released too often to stick on single version.
-uwsgi
-
-gunicorn==19.0
-meinheld==0.5.6
-
-# Tornado
-tornado==3.2.2
-motor==0.3
-# pymongo is installed via motor dependency
-
-# Django
-Django==1.6.5
-
-# Flask
-Jinja2==2.7.3
-Werkzeug==0.9.6
-flask==0.10.1
-SQLAlchemy==0.9.4
-Flask-SQLAlchemy==1.0
-
-# Bottle
-bottle==0.12.7
-bottle-sqlalchemy==0.4.1
-
-# Falcon
-Cython==0.20.1
-falcon==0.1.8
-
-Momoko==1.1.3

+ 0 - 4
config/uwsgi_stop.ini

@@ -1,4 +0,0 @@
-# If uWSGI was started with --ini uwsgi.ini, it can be stopped with --ini uwsgi_stop.ini
-
-[uwsgi]
-stop = /tmp/uwsgi.pid

+ 14 - 0
django/bash_profile.sh

@@ -0,0 +1,14 @@
+export PY2_ROOT=$IROOT/py2
+export PY2=$PY2_ROOT/bin/python
+export PY2_PIP=$PY2_ROOT/bin/pip
+export PY2_GUNICORN=$PY2_ROOT/bin/gunicorn
+
+export PYPY_ROOT=$IROOT/pypy
+export PYPY=$PYPY_ROOT/bin/python
+export PYPY_PIP=$PYPY_ROOT/bin/pip
+export PYPY_GUNICORN=$PYPY_ROOT/bin/gunicorn
+
+export PY3_ROOT=$IROOT/py3
+export PY3=$PY3_ROOT/bin/python
+export PY3_PIP=$PY3_ROOT/bin/pip3
+export PY3_GUNICORN=$PY3_ROOT/bin/gunicorn

+ 7 - 7
django/benchmark_config

@@ -5,7 +5,7 @@
       "setup_file": "setup",
       "json_url": "/json",
       "db_url": "/db",
-      "query_url": "/db?queries=",
+      "query_url": "/dbs?queries=",
       "fortune_url": "/fortunes",
       "update_url": "/update?queries=",
       "port": 8080,
@@ -15,7 +15,7 @@
       "framework": "django",
       "language": "Python",
       "orm": "Full",
-      "platform": "Gunicorn/Meinheld",
+      "platform": "Meinheld",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
@@ -27,7 +27,7 @@
       "setup_file": "setup_py3",
       "json_url": "/json",
       "db_url": "/db",
-      "query_url": "/db?queries=",
+      "query_url": "/dbs?queries=",
       "fortune_url": "/fortunes",
       "update_url": "/update?queries=",
       "port": 8080,
@@ -37,19 +37,19 @@
       "framework": "django",
       "language": "Python",
       "orm": "Full",
-      "platform": "Gunicorn/Meinheld",
+      "platform": "Meinheld",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "django-py3",
-      "notes": "CPython 3.3",
+      "notes": "CPython 3.4",
       "versus": "wsgi"
     },
     "postgresql": {
       "setup_file": "setup_pg",
       "json_url": "/json",
       "db_url": "/db",
-      "query_url": "/db?queries=",
+      "query_url": "/dbs?queries=",
       "fortune_url": "/fortunes",
       "update_url": "/update?queries=",
       "port": 8080,
@@ -59,7 +59,7 @@
       "framework": "django",
       "language": "Python",
       "orm": "Full",
-      "platform": "Gunicorn/Meinheld",
+      "platform": "Meinheld",
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",

+ 21 - 0
django/gunicorn_conf.py

@@ -0,0 +1,21 @@
+import multiprocessing
+import sys
+
+_is_pypy = hasattr(sys, 'pypy_version_info')
+
+workers = multiprocessing.cpu_count() * 3
+bind = "0.0.0.0:8080"
+keepalive = 120
+errorlog = '-'
+pythonpath = 'hello'
+
+if _is_pypy:
+    worker_class = "tornado"
+else:
+    worker_class = "meinheld.gmeinheld.MeinheldWorker"
+
+    def post_fork(server, worker):
+        # Disalbe access log
+        import meinheld.server
+        meinheld.server.set_access_logger(None)
+

+ 2 - 2
django/hello/hello/settings.py

@@ -17,7 +17,7 @@ DATABASES = {
         'NAME': 'hello_world',           # Or path to database file if using sqlite3.
         'USER': 'benchmarkdbuser',       # Not used with sqlite3.
         'PASSWORD': 'benchmarkdbpass',   # Not used with sqlite3.
-        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
+        'HOST': os.environ.get('TFB_DATABASE_HOST', ''),  # Set to empty string for localhost. Not used with sqlite3.
         'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
         'CONN_MAX_AGE': 30,
     }
@@ -111,7 +111,7 @@ TEMPLATE_DIRS = (
     # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
     # Always use forward slashes, even on Windows.
     # Don't forget to use absolute paths, not relative paths.
-    os.path.expanduser("~/FrameworkBenchmarks/django/hello/templates"),
+    os.path.expandvars("$TROOT/hello/templates"),
 )
 
 INSTALLED_APPS = (

+ 1 - 0
django/hello/hello/urls.py

@@ -16,6 +16,7 @@ urlpatterns = patterns('',
     # url(r'^admin/', include(admin.site.urls)),
     url(r'^json$', 'world.views.json'),
     url(r'^db$', 'world.views.db'),
+    url(r'^dbs$', 'world.views.dbs'),
     url(r'^fortunes$', 'world.views.fortunes'),
     url(r'^update$', 'world.views.update'),
 )

+ 24 - 17
django/hello/world/views.py

@@ -14,6 +14,18 @@ from functools import partial
 if sys.version_info[0] == 3:
   xrange = range
 
+def _get_queries(request):
+  try:
+    queries = int(request.GET.get('queries', 1))
+  except Exception:
+    queries = 1
+  if queries < 1:
+    queries = 1
+  if queries > 500:
+    queries = 500
+  return queries
+
+
 def json(request):
   response = {
     "message": "Hello, World!"
@@ -21,33 +33,29 @@ def json(request):
   return HttpResponse(uj_dumps(response), mimetype="application/json")
 
 def db(request):
-  queries = int(request.GET.get('queries', 1))
-  # worlds = []
-  # it's not required to explicitly loop instead of using list comprehension
-  #for i in range(queries):
-    # get a random row, we know the ids are between 1 and 10000
-    #worlds.append(World.objects.get(id=random.randint(1, 10000)))
-  # instead we can do:
-  #worlds = [World.objects.get(id=random.randint(1, 10000)) for i in range(queries)]
-  
+  r = random.randint(1, 10000)
+  world = uj_dumps({'id' : r, 'randomNumber' : World.objects.get(id=r).randomnumber})
+  return HttpResponse(world, mimetype="application/json")
+
+def dbs(request):
+  queries = _get_queries(request)
+
   # fun fact:  every dot-notation lookup calls some python magic under the hood.  Like every other code,
   # 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!  if we're calling a function over and over with the same parameters, 
   # we can use even more function magic.
+  #r = random.randint
   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
   # by creating dicts, we don't need to user the model serializer, which is probably slow and only appropriate
   # for complicated serializations of joins and crazy query sets etc
   # test xrange vs range if the query number is gigantic
-  if queries == 1:
-    r = random.randint(1,10000)
-    worlds = uj_dumps({'id' : r, 'randomNumber' : g(id=r).randomnumber})
-  else:
-    worlds = uj_dumps([{'id' : r, 'randomNumber' : g(id=r).randomnumber} for r in [rp() for q in xrange(queries)]])
+  worlds = uj_dumps([{'id' : r, 'randomNumber' : g(id=r).randomnumber} for r in [rp() for q in xrange(queries)]])
   return HttpResponse(worlds, mimetype="application/json")
 
 def fortunes(request):
@@ -60,7 +68,7 @@ def fortunes(request):
   return render(request, 'fortunes.html', context)
 
 def update(request):
-  queries = int(request.GET.get('queries', 1))
+  queries = _get_queries(request)
   g = World.objects.get
   rp = partial(random.randint, 1, 10000)
   
@@ -69,7 +77,6 @@ def update(request):
     w = g(id=r)
     w.randomnumber=rp()
     w.save()
-
     worlds.append({'id' : r, 'randomNumber' : w.randomnumber})
 
   return HttpResponse(uj_dumps(worlds), mimetype="application/json")

+ 6 - 0
django/install.sh

@@ -1,3 +1,9 @@
 #!/bin/bash
 
+mkdir -p ~/.pip_cache
+export PIP_DOWNLOAD_CACHE=$HOME/.pip_cache
+
 fw_depends python2 python3
+
+$IROOT/py2/bin/pip install -r $TROOT/requirements.txt
+$IROOT/py3/bin/pip3 install -r $TROOT/requirements.txt

+ 8 - 0
django/requirements.txt

@@ -0,0 +1,8 @@
+Django==1.6.5
+
+mysqlclient==1.3.1
+psycopg2==2.5.3
+ujson==1.33
+
+gunicorn==19.1
+meinheld==0.5.6

+ 7 - 29
django/setup.py

@@ -1,37 +1,15 @@
 import subprocess
-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, logfile, errfile):
-    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)
-    env = os.environ.copy()
-    env['DJANGO_DB'] = 'mysql'
-    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",
-        env=env, stderr=errfile, stdout=logfile)
+    subprocess.Popen(
+        "$PY2_GUNICORN --pid=gunicorn.pid hello.wsgi:application -c gunicorn_conf.py --env DJANGO_DB=mysql",
+        cwd="django", shell=True, stderr=errfile, stdout=logfile)
     return 0
 
+
 def stop(logfile, errfile):
-    global proc
-    if proc is None:
-        return 0
-    proc.terminate()
-    proc.wait()
-    proc = None
+    subprocess.call(
+        "kill `cat gunicorn.pid`",
+        cwd="django", shell=True, stderr=errfile, stdout=logfile)
     return 0

+ 7 - 29
django/setup_pg.py

@@ -1,37 +1,15 @@
 import subprocess
-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, logfile, errfile):
-    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)
-    env = os.environ.copy()
-    env['DJANGO_DB'] = 'postgresql_psycopg2'
-    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",
-        env=env, stderr=errfile, stdout=logfile)
+    subprocess.Popen(
+        "$PY2_GUNICORN --pid=gunicorn.pid hello.wsgi:application -c gunicorn_conf.py --env DJANGO_DB=postgresql_psycopg2",
+        cwd="django", shell=True, stderr=errfile, stdout=logfile)
     return 0
 
+
 def stop(logfile, errfile):
-    global proc
-    if proc is None:
-        return 0
-    proc.terminate()
-    proc.wait()
-    proc = None
+    subprocess.call(
+        "kill `cat gunicorn.pid`",
+        cwd="django", shell=True, stderr=errfile, stdout=logfile)
     return 0

+ 7 - 29
django/setup_py3.py

@@ -1,37 +1,15 @@
 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, logfile, errfile):
-    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)
-    env = os.environ.copy()
-    env['DJANGO_DB'] = 'mysql'
-    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",
-        env=env, stderr=errfile, stdout=logfile)
+    subprocess.Popen(
+        "$PY3_GUNICORN --pid=gunicorn.pid hello.wsgi:application -c gunicorn_conf.py --env DJANGO_DB=mysql",
+        cwd="django", shell=True, stderr=errfile, stdout=logfile)
     return 0
 
+
 def stop(logfile, errfile):
-    global proc
-    if proc is None:
-        return 0
-    proc.terminate()
-    proc.wait()
-    proc = None
+    subprocess.call(
+        "kill `cat gunicorn.pid`",
+        cwd="django", shell=True, stderr=errfile, stdout=logfile)
     return 0

+ 14 - 0
falcon/bash_profile.sh

@@ -0,0 +1,14 @@
+export PY2_ROOT=$IROOT/py2
+export PY2=$PY2_ROOT/bin/python
+export PY2_PIP=$PY2_ROOT/bin/pip
+export PY2_GUNICORN=$PY2_ROOT/bin/gunicorn
+
+export PYPY_ROOT=$IROOT/pypy
+export PYPY=$PYPY_ROOT/bin/python
+export PYPY_PIP=$PYPY_ROOT/bin/pip
+export PYPY_GUNICORN=$PYPY_ROOT/bin/gunicorn
+
+export PY3_ROOT=$IROOT/py3
+export PY3=$PY3_ROOT/bin/python
+export PY3_PIP=$PY3_ROOT/bin/pip3
+export PY3_GUNICORN=$PY3_ROOT/bin/gunicorn

+ 2 - 0
falcon/gunicorn_conf.py

@@ -7,6 +7,8 @@ _is_pypy = hasattr(sys, 'pypy_version_info')
 workers = multiprocessing.cpu_count()
 bind = "0.0.0.0:8080"
 keepalive = 120
+errorlog = '-'
+pidfile = 'gunicorn.pid'
 
 if _is_pypy:
     worker_class = "tornado"

+ 7 - 0
falcon/install.sh

@@ -1,3 +1,10 @@
 #!/bin/bash
 
+mkdir -p ~/.pip_cache
+export PIP_DOWNLOAD_CACHE=$HOME/.pip_cache
+
 fw_depends python2 python3 pypy
+
+$IROOT/py2/bin/pip install -r $TROOT/requirements.txt
+$IROOT/py3/bin/pip3 install -r $TROOT/requirements.txt
+$IROOT/pypy/bin/pip install -r $TROOT/requirements-pypy.txt

+ 3 - 0
falcon/requirements-pypy.txt

@@ -0,0 +1,3 @@
+gunicorn==19.1
+tornado==3.2.2
+falcon==0.1.9

+ 4 - 0
falcon/requirements.txt

@@ -0,0 +1,4 @@
+gunicorn==19.1
+meinheld==0.5.6
+Cython==0.20.1
+falcon==0.1.9

+ 7 - 21
falcon/setup.py

@@ -1,32 +1,18 @@
 import subprocess
 import os
 
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
-
-proc = None
+CWD = os.path.dirname(__file__)
 
 
 def start(args, logfile, errfile):
-    global proc
-    proc = subprocess.Popen([
-        bin_dir + "/gunicorn",
-        "app:app",
-        "-c", "gunicorn_conf.py"],
-        cwd="falcon", stderr=errfile, stdout=logfile)
+    subprocess.Popen(
+        "$PY2_GUNICORN app:app -c gunicorn_conf.py",
+        cwd=CWD, stderr=errfile, stdout=logfile, shell=True)
     return 0
 
 
 def stop(logfile, errfile):
-    global proc
-    if proc:
-        proc.terminate()
-        proc = None
-
-    p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
-    out, err = p.communicate()
-    for line in out.splitlines():
-      if 'FrameworkBenchmarks/installs/py2/bin/' in line:
-        errfile.write("Killing: " + line + "\n")
-        pid = int(line.split()[1])
-        os.kill(pid, 15)
+    subprocess.call(
+        "kill `cat gunicorn.pid`",
+        cwd=CWD, stderr=errfile, stdout=logfile, shell=True)
     return 0

+ 6 - 16
falcon/setup_py3.py

@@ -1,25 +1,15 @@
 import subprocess
-import os
-
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py3/bin')
-
-proc = None
 
 
 def start(args, logfile, errfile):
-    global proc
-    proc = subprocess.Popen([
-        bin_dir + "/gunicorn",
-        "app:app",
-        "-c", "gunicorn_conf.py"],
-        cwd="falcon", stderr=errfile, stdout=logfile)
+    subprocess.Popen(
+        "$PY3_GUNICORN app:app -c gunicorn_conf.py",
+        cwd="falcon", stderr=errfile, stdout=logfile, shell=True)
     return 0
 
 
 def stop(logfile, errfile):
-    global proc
-    if proc is None:
-        return 0
-    proc.terminate()
-    proc = None
+    subprocess.call(
+        "kill `cat gunicorn.pid`",
+        cwd="falcon", stderr=errfile, stdout=logfile, shell=True)
     return 0

+ 7 - 23
falcon/setup_pypy.py

@@ -1,31 +1,15 @@
 import subprocess
-import os
-
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/pypy/bin')
-
-proc = None
 
 
 def start(args, logfile, errfile):
-    global proc
-    proc = subprocess.Popen([
-        bin_dir + "/gunicorn",
-        "app:app",
-        "-c", "gunicorn_conf.py"],
-        cwd="falcon", stderr=errfile, stdout=logfile)
+    subprocess.Popen(
+        "$PYPY_GUNICORN app:app -c gunicorn_conf.py",
+        cwd="falcon", stderr=errfile, stdout=logfile, shell=True)
     return 0
 
-def stop(logfile, errfile):
-    global proc
-    if proc:
-        proc.terminate()
-        proc = None
 
-    p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
-    out, err = p.communicate()
-    for line in out.splitlines():
-      if 'installs/pypy/bin/' in line:
-        errfile.write("Killing: " + line + "\n")
-        pid = int(line.split()[1])
-        os.kill(pid, 15)
+def stop(logfile, errfile):
+    subprocess.call(
+        "kill `cat gunicorn.pid`",
+        cwd="falcon", stderr=errfile, stdout=logfile, shell=True)
     return 0

+ 16 - 1
flask/app.py

@@ -2,6 +2,7 @@
 from functools import partial
 import json
 from operator import attrgetter
+import os
 from random import randint
 import sys
 
@@ -13,6 +14,8 @@ from sqlalchemy import create_engine
 if sys.version_info[0] == 3:
     xrange = range
 
+DBHOST = os.environ.get('TFB_DATABASE_HOST', 'localhost')
+
 try:
     import MySQLdb
     mysql_schema = "mysql:"
@@ -22,7 +25,7 @@ except ImportError:
 # setup
 
 app = Flask(__name__)
-app.config['SQLALCHEMY_DATABASE_URI'] = mysql_schema + '//benchmarkdbuser:benchmarkdbpass@localhost:3306/hello_world?charset=utf8'
+app.config['SQLALCHEMY_DATABASE_URI'] = mysql_schema + '//benchmarkdbuser:benchmarkdbpass@%s:3306/hello_world?charset=utf8' % DBHOST
 app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False
 db = SQLAlchemy(app)
 dbraw_engine = create_engine(app.config['SQLALCHEMY_DATABASE_URI'], connect_args={'autocommit': True}, pool_reset_on_return=None)
@@ -67,6 +70,10 @@ def hello():
 @app.route("/db")
 def get_random_world():
     num_queries = request.args.get("queries", 1, type=int)
+    if num_queries < 1:
+        num_queries = 1
+    if num_queries > 500:
+        num_queries = 500
     worlds = [World.query.get(randint(1, 10000)).serialize
               for _ in xrange(num_queries)]
     return json_response(worlds)
@@ -83,6 +90,10 @@ def get_random_world_single():
 def get_random_world_raw():
     connection = dbraw_engine.connect()
     num_queries = request.args.get("queries", 1, type=int)
+    if num_queries < 1:
+        num_queries = 1
+    if num_queries > 500:
+        num_queries = 500
     worlds = []
     for i in xrange(num_queries):
         wid = randint(1, 10000)
@@ -122,6 +133,8 @@ def get_forutens_raw():
 def updates():
     """Test 5: Database Updates"""
     num_queries = request.args.get('queries', 1, type=int)
+    if num_queries < 1:
+        num_queries = 1
     if num_queries > 500:
         num_queries = 500
 
@@ -143,6 +156,8 @@ def raw_updates():
     connection = dbraw_engine.connect()
     try:
         num_queries = request.args.get('queries', 1, type=int)
+        if num_queries < 1:
+            num_queries = 1
         if num_queries > 500:
             num_queries = 500
 

+ 14 - 0
flask/bash_profile.sh

@@ -0,0 +1,14 @@
+export PY2_ROOT=$IROOT/py2
+export PY2=$PY2_ROOT/bin/python
+export PY2_PIP=$PY2_ROOT/bin/pip
+export PY2_GUNICORN=$PY2_ROOT/bin/gunicorn
+
+export PYPY_ROOT=$IROOT/pypy
+export PYPY=$PYPY_ROOT/bin/python
+export PYPY_PIP=$PYPY_ROOT/bin/pip
+export PYPY_GUNICORN=$PYPY_ROOT/bin/gunicorn
+
+export PY3_ROOT=$IROOT/py3
+export PY3=$PY3_ROOT/bin/python
+export PY3_PIP=$PY3_ROOT/bin/pip3
+export PY3_GUNICORN=$PY3_ROOT/bin/gunicorn

+ 20 - 0
flask/gunicorn_conf.py

@@ -0,0 +1,20 @@
+import multiprocessing
+import sys
+
+_is_pypy = hasattr(sys, 'pypy_version_info')
+
+workers = multiprocessing.cpu_count() * 3
+bind = "0.0.0.0:8080"
+keepalive = 120
+errorlog = '-'
+
+if _is_pypy:
+    worker_class = "tornado"
+else:
+    worker_class = "meinheld.gmeinheld.MeinheldWorker"
+
+    def post_fork(server, worker):
+        # Disalbe access log
+        import meinheld.server
+        meinheld.server.set_access_logger(None)
+

+ 8 - 1
flask/install.sh

@@ -1,3 +1,10 @@
 #!/bin/bash
 
-fw_depends python2 python3 pypy
+mkdir -p ~/.pip_cache
+export PIP_DOWNLOAD_CACHE=$HOME/.pip_cache
+
+fw_depends python2 python3 pypy nginx
+
+$IROOT/py2/bin/pip install -r $TROOT/requirements.txt
+$IROOT/py3/bin/pip3 install -r $TROOT/requirements.txt
+$IROOT/pypy/bin/pip install -r $TROOT/requirements-pypy.txt

+ 48 - 0
flask/nginx.conf

@@ -0,0 +1,48 @@
+# This file is based on /usr/local/nginx/conf/nginx.conf.default.
+
+# One worker process per core
+error_log stderr error;
+
+events {
+    # This needed to be increased because the nginx error log said so.
+    # http://nginx.org/en/docs/ngx_core_module.html#worker_connections
+    worker_connections  65535;
+    multi_accept on;
+}
+
+http {
+    default_type  application/octet-stream;
+    client_body_temp_path      /tmp;
+
+    # turn off request logging for performance
+    access_log off;
+
+    # I think these only options affect static file serving
+    sendfile        on;
+    tcp_nopush      on;
+
+    # Allow many HTTP Keep-Alive requests in a single TCP connection before
+    # closing it (the default is 100). This will minimize the total number
+    # of TCP connections opened/closed. The problem is that this may cause
+    # some worker processes to be handling too connections relative to the
+    # other workers based on an initial imbalance, so this is disabled for
+    # now.
+#    keepalive_requests 1000;
+
+    #keepalive_timeout  0;
+    keepalive_timeout  65;
+
+    server {
+        # For information on deferred, see:
+        # http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
+        # http://www.techrepublic.com/article/take-advantage-of-tcp-ip-options-to-optimize-data-transmission/
+        # The backlog argument to listen() is set to match net.ipv4.tcp_max_syn_backlog and net.core.somaxconn
+        listen       8080 default_server deferred backlog=65535;
+        server_name  localhost;
+
+        location / {
+            uwsgi_pass unix:/tmp/uwsgi.sock;
+            include /usr/local/nginx/conf/uwsgi_params;
+        }
+    }    
+}

+ 8 - 0
flask/requirements-pypy.txt

@@ -0,0 +1,8 @@
+Jinja2==2.7.3
+Werkzeug==0.9.6
+flask==0.10.1
+SQLAlchemy==0.9.7
+Flask-SQLAlchemy==1.0
+mysqlclient==1.3.1
+gunicorn==19.1
+tornado==3.2.2

+ 9 - 1
flask/requirements.txt

@@ -1 +1,9 @@
-Chaussette
+Jinja2==2.7.3
+Werkzeug==0.9.6
+flask==0.10.1
+SQLAlchemy==0.9.7
+Flask-SQLAlchemy==1.0
+mysqlclient==1.3.1
+gunicorn==19.1
+meinheld==0.5.6
+uwsgi

+ 15 - 27
flask/setup.py

@@ -1,43 +1,31 @@
 import subprocess
-import setup_util
-import multiprocessing
 import os
-
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
-NCPU = multiprocessing.cpu_count()
-
-CIRCUS_INI = """\
-[watcher:app]
-cmd = {BIN}/chaussette --fd=$(circus.sockets.app) --backend=meinheld app.app
-use_sockets = True
-numprocesses = {PROCS}
-
-[socket:app]
-host = 0.0.0.0
-port = 8080
-"""
+import time
 
 proc = None
 
+
 def start(args, logfile, errfile):
     global proc
-
-    subprocess.check_call(bin_dir + "/pip install -r requirements.txt",
-                          cwd="flask", stderr=errfile, stdout=logfile, shell=True)
-
-    with open("flask/circus.ini", "w") as f:
-        f.write(CIRCUS_INI.format(BIN=bin_dir, PROCS=NCPU*3))
-
-    setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
-    proc = subprocess.Popen([bin_dir + "/circusd", "circus.ini"],
-		            cwd="flask", stderr=errfile, stdout=logfile)
+    proc = subprocess.Popen(
+        "exec $PY2_GUNICORN app:app -c gunicorn_conf.py",
+        cwd="flask", stderr=errfile, stdout=logfile, shell=True)
     return 0
 
+
 def stop(logfile, errfile):
     global proc
     if proc is None:
         return 0
     proc.terminate()
-    proc.wait()
     proc = None
+    time.sleep(1)
+
+    p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+    out, err = p.communicate()
+    for line in out.splitlines():
+      if 'gunicorn' in line:
+        errfile.write("Killing: " + line + "\n")
+        pid = int(line.split()[1])
+        os.kill(pid, 15)
     return 0

+ 10 - 12
flask/setup_nginxuwsgi.py

@@ -1,36 +1,34 @@
 import subprocess
 import multiprocessing
 import os
-import setup_util
 
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
-config_dir = os.path.expanduser('~/FrameworkBenchmarks/config')
+
+CWD = os.path.abspath(os.path.dirname(__file__))
+bin_dir = os.path.expandvars('$PY2_ROOT/bin')
 NCPU = multiprocessing.cpu_count()
-NGINX_COMMAND = 'sudo /usr/local/nginx/sbin/nginx -c ' + config_dir + '/nginx_uwsgi.conf'
+NGINX_COMMAND = 'sudo /usr/local/nginx/sbin/nginx -c ' + CWD + '/nginx.conf'
 
 
 def start(args, logfile, errfile):
-    setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
     try:
         subprocess.call(
             NGINX_COMMAND,
             shell=True, stdout=logfile, stderr=errfile)
 
         # Run in the background, but keep stdout/stderr for easy debugging
-        subprocess.Popen(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi.ini' +
-            ' --processes ' + str(NCPU * 3) +
-            ' --wsgi app:app',
-            shell=True, cwd='flask', stderr=errfile, stdout=logfile)
+        subprocess.Popen(
+            "{0}/uwsgi --ini uwsgi.ini --processes {1} --wsgi app:app".format(bin_dir, NCPU*3),
+            shell=True, cwd=CWD, stderr=errfile, stdout=logfile)
         return 0
     except subprocess.CalledProcessError:
         return 1
 
+
 def stop(logfile, errfile):
     subprocess.call(
         NGINX_COMMAND + ' -s stop',
         shell=True, stdout=logfile, stderr=errfile)
-    subprocess.call(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi_stop.ini', shell=True, stderr=errfile, stdout=logfile)
 
-    os.system('killall nginx')
-    os.system('killall uwsgi')
+    subprocess.call(bin_dir + '/uwsgi --stop /tmp/uwsgi.pid',
+                    shell=True, stderr=errfile, stdout=logfile)
     return 0

+ 14 - 27
flask/setup_py3.py

@@ -1,44 +1,31 @@
 import subprocess
-import setup_util
-import multiprocessing
 import os
-
-PY2BIN = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
-PY3BIN = os.path.expanduser('~/FrameworkBenchmarks/installs/py3/bin')
-NCPU = multiprocessing.cpu_count()
-
-CIRCUS_INI = """\
-[watcher:app]
-cmd = {BIN}/chaussette --fd=$(circus.sockets.app) --backend=meinheld app.app
-use_sockets = True
-numprocesses = {PROCS}
-
-[socket:app]
-host = 0.0.0.0
-port = 8080
-"""
+import time
 
 proc = None
 
 
 def start(args, logfile, errfile):
     global proc
-
-    subprocess.check_call(PY3BIN + "/pip3 install -r requirements.txt",
-                          cwd="flask", stderr=errfile, stdout=logfile, shell=True)
-
-    with open("flask/circus.ini", "w") as f:
-        f.write(CIRCUS_INI.format(BIN=PY3BIN, PROCS=NCPU*3))
-
-    setup_util.replace_text("flask/app.py", "DBHOSTNAME", args.database_host)
-    proc = subprocess.Popen([PY2BIN + "/circusd", "circus.ini"],
-		            cwd="flask", stderr=errfile, stdout=logfile)
+    proc = subprocess.Popen(
+        "exec $PY3_GUNICORN app:app -c gunicorn_conf.py",
+        cwd="flask", stderr=errfile, stdout=logfile, shell=True)
     return 0
 
+
 def stop(logfile, errfile):
     global proc
     if proc is None:
         return 0
     proc.terminate()
     proc = None
+    time.sleep(1)
+
+    p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+    out, err = p.communicate()
+    for line in out.splitlines():
+      if 'gunicorn' in line:
+        errfile.write("Killing: " + line + "\n")
+        pid = int(line.split()[1])
+        os.kill(pid, 15)
     return 0

+ 22 - 14
flask/setup_pypy.py

@@ -1,31 +1,39 @@
 import subprocess
-import setup_util
-import multiprocessing
 import os
-
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/pypy/bin')
-NCPU = multiprocessing.cpu_count()
+import time
 
 proc = None
 
 
 def start(args, logfile, errfile):
     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", stderr=errfile, stdout=logfile)
+    proc = subprocess.Popen(
+        "exec $PYPY_GUNICORN app:app -c gunicorn_conf.py",
+        cwd="flask", stderr=errfile, stdout=logfile, shell=True)
     return 0
 
+
 def stop(logfile, errfile):
     global proc
     if proc is None:
         return 0
     proc.terminate()
+    for _ in range(10):
+        if proc.poll() is not None:
+            break
+        time.sleep(0.2)
+
+    if proc.poll() is None:
+        proc.kill()
+
     proc = None
+
+    p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+    out, err = p.communicate()
+    for line in out.splitlines():
+      if 'gunicorn' in line:
+        errfile.write("Killing: " + line + "\n")
+        pid = int(line.split()[1])
+        os.kill(pid, 15)
     return 0
+

+ 19 - 0
flask/uwsgi.ini

@@ -0,0 +1,19 @@
+[uwsgi]
+master
+; Increase listen queue used for nginx connecting to uWSGI. This matches
+; net.ipv4.tcp_max_syn_backlog and net.core.somaxconn.
+listen = 65535
+; for performance
+disable-logging
+; use UNIX sockets instead of TCP loopback for performance
+socket = /tmp/uwsgi.sock
+; allow nginx to access the UNIX socket
+chmod-socket = 666
+; Avoid thundering herd problem http://uwsgi-docs.readthedocs.org/en/latest/articles/SerializingAccept.html .
+; This is currently disabled because when I tried it with flask, it caused a
+; 20% performance hit. The CPU cores could not be saturated with thunder-lock.
+; I'm not yet sure the full story, so this is presently disabled. Also,
+; disabling this caused bottle to get ~13% faster.
+;thunder-lock
+; used by uwsgi_stop.ini
+pidfile = /tmp/uwsgi.pid

+ 14 - 0
pyramid/bash_profile.sh

@@ -0,0 +1,14 @@
+export PY2_ROOT=$IROOT/py2
+export PY2=$PY2_ROOT/bin/python
+export PY2_PIP=$PY2_ROOT/bin/pip
+export PY2_GUNICORN=$PY2_ROOT/bin/gunicorn
+
+export PYPY_ROOT=$IROOT/pypy
+export PYPY=$PYPY_ROOT/bin/python
+export PYPY_PIP=$PYPY_ROOT/bin/pip
+export PYPY_GUNICORN=$PYPY_ROOT/bin/gunicorn
+
+export PY3_ROOT=$IROOT/py3
+export PY3=$PY3_ROOT/bin/python3
+export PY3_PIP=$PY3_ROOT/bin/pip3
+export PY3_GUNICORN=$PY3_ROOT/bin/gunicorn

+ 4 - 4
pyramid/benchmark_config

@@ -16,8 +16,8 @@
       "framework": "pyramid",
       "language": "Python",
       "orm": "Full",
-      "platform": "wsgi",
-      "webserver": "Gunicorn",
+      "platform": "Meinheld",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "pyramid-py2",
@@ -39,8 +39,8 @@
       "framework": "pyramid",
       "language": "Python",
       "orm": "Full",
-      "platform": "wsgi",
-      "webserver": "Gunicorn",
+      "platform": "Meinheld",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "pyramid-py3",

+ 2 - 1
pyramid/frameworkbenchmarks/models.py

@@ -3,6 +3,7 @@ Benchmark models.
 """
 
 import json
+import os
 import psycopg2
 from collections import Iterable
 from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
@@ -10,7 +11,7 @@ from sqlalchemy.orm import sessionmaker
 from sqlalchemy.pool import QueuePool
 from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
 
-DBHOSTNAME = 'localhost'
+DBHOSTNAME = os.environ.get('TFB_DATABASE_HOST', 'localhost')
 
 def get_conn():
     return psycopg2.connect(

+ 21 - 0
pyramid/gunicorn_conf.py

@@ -0,0 +1,21 @@
+import multiprocessing
+import sys
+
+_is_pypy = hasattr(sys, 'pypy_version_info')
+
+workers = multiprocessing.cpu_count() * 3
+bind = "0.0.0.0:8080"
+keepalive = 120
+errorlog = '-'
+pidfile = 'gunicorn.pid'
+
+if _is_pypy:
+    worker_class = "tornado"
+else:
+    worker_class = "meinheld.gmeinheld.MeinheldWorker"
+
+    def post_fork(server, worker):
+        # Disalbe access log
+        import meinheld.server
+        meinheld.server.set_access_logger(None)
+

+ 7 - 1
pyramid/install.sh

@@ -1,3 +1,9 @@
 #!/bin/bash
 
-fw_depends python2 python3 pypy
+mkdir -p ~/.pip_cache
+export PIP_DOWNLOAD_CACHE=$HOME/.pip_cache
+
+fw_depends python2 python3
+
+$IROOT/py2/bin/pip install -r $TROOT/requirements.txt
+$IROOT/py3/bin/pip3 install -r $TROOT/requirements.txt

+ 6 - 0
pyramid/requirements.txt

@@ -0,0 +1,6 @@
+psycopg2==2.5.3
+gunicorn==19.1
+meinheld==0.5.6
+SQLAlchemy==0.9.7
+pyramid==1.5.1
+pyramid_chameleon==0.3

+ 0 - 34
pyramid/setup_benchmark.py

@@ -1,34 +0,0 @@
-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(
-        "frameworkbenchmarks/models.py",
-        "DBHOSTNAME = 'localhost'",
-        "DBHOSTNAME = '%s'" % args.database_host
-    )
-    proc = subprocess.Popen([
-        bin_dir + '/gunicorn',
-        'wsgi:app',
-        '-b', "0.0.0.0:6543",
-        '-w', str(NCPU*3)],
-        cwd='pyramid'
-    )
-    return 0
-
-def stop():
-    global proc
-    if proc is not None:
-        proc.terminate()
-        proc.wait()
-    return 0

+ 8 - 26
pyramid/setup_py2.py

@@ -1,36 +1,18 @@
 import subprocess
-import setup_util
-import multiprocessing
 import os
 
-home = os.path.expanduser('~')
-pyramid_dir = home + "/FrameworkBenchmarks/pyramid"
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
-NCPU = multiprocessing.cpu_count()
-
-proc = None
+CWD = os.path.abspath(os.path.dirname(__file__))
 
 
 def start(args, logfile, errfile):
-    global proc
-    setup_util.replace_text(
-        pyramid_dir + "/frameworkbenchmarks/models.py",
-        "DBHOSTNAME = 'localhost'",
-        "DBHOSTNAME = '%s'" % args.database_host
-    )
-    subprocess.call(bin_dir + '/pip install -e .', cwd='pyramid', shell=True, stderr=errfile, stdout=logfile)
-    proc = subprocess.Popen([
-        bin_dir + '/gunicorn',
-        'wsgi:app',
-        '-b', "0.0.0.0:6543",
-        '-w', str(NCPU*3)],
-        cwd='pyramid', stderr=errfile, stdout=logfile
-    )
+    subprocess.Popen(
+        "$PY2_GUNICORN wsgi:app -c gunicorn_conf.py",
+        cwd=CWD, sehll=True, stderr=errfile, stdout=logfile)
     return 0
 
+
 def stop(logfile, errfile):
-    global proc
-    if proc is not None:
-        proc.terminate()
-        proc.wait()
+    subprocess.call(
+        "kill `cat gunicorn.pid`",
+        cwd=CWD, stderr=errfile, stdout=logfile, shell=True)
     return 0

+ 8 - 26
pyramid/setup_py3.py

@@ -1,36 +1,18 @@
 import subprocess
-import setup_util
-import multiprocessing
 import os
 
-home = os.path.expanduser('~')
-pyramid_dir = home + "/FrameworkBenchmarks/pyramid"
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py3/bin')
-NCPU = multiprocessing.cpu_count()
-
-proc = None
+CWD = os.path.abspath(os.path.dirname(__file__))
 
 
 def start(args, logfile, errfile):
-    global proc
-    setup_util.replace_text(
-        pyramid_dir + "/frameworkbenchmarks/models.py",
-        "DBHOSTNAME = 'localhost'",
-        "DBHOSTNAME = '%s'" % args.database_host
-    )
-    subprocess.call(bin_dir + '/pip install -e .', cwd='pyramid', shell=True, stderr=errfile, stdout=logfile)
-    proc = subprocess.Popen([
-        bin_dir + '/gunicorn',
-        'wsgi:app',
-        '-b', "0.0.0.0:6543",
-        '-w', str(NCPU*3)],
-        cwd='pyramid', stderr=errfile, stdout=logfile
-    )
+    subprocess.Popen(
+        "$PY3_GUNICORN wsgi:app -c gunicorn_conf.py",
+        cwd=CWD, sehll=True, stderr=errfile, stdout=logfile)
     return 0
 
+
 def stop(logfile, errfile):
-    global proc
-    if proc is not None:
-        proc.terminate()
-        proc.wait()
+    subprocess.call(
+        "kill `cat gunicorn.pid`",
+        cwd=CWD, stderr=errfile, stdout=logfile, shell=True)
     return 0

+ 4 - 4
toolset/run-ci.py

@@ -288,12 +288,12 @@ class CIRunnner:
     sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 || gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 7F0CEB10
     echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list
 
-    sudo apt-get update
+    sudo apt-get -q update
     
     # MongoDB takes a good 30-45 seconds to turn on, so install it first
-    sudo apt-get install mongodb-org
+    sudo apt-get -q install mongodb-org
 
-    sudo apt-get install openssh-server
+    sudo apt-get -q install openssh-server
 
     # Run as travis user (who already has passwordless sudo)
     ssh-keygen -f /home/travis/.ssh/id_rsa -N '' -t rsa
@@ -408,4 +408,4 @@ if __name__ == "__main__":
     sys.exit(retcode)
 
 
-# vim: set sw=2 ts=2 expandtab
+# vim: set sw=2 ts=2 expandtab

+ 4 - 4
toolset/setup/linux/languages/pypy.sh

@@ -7,7 +7,7 @@ fw_get https://bitbucket.org/pypy/pypy/downloads/pypy-2.3.1-linux64.tar.bz2 -O p
 fw_untar pypy-2.3.1-linux64.tar.bz2
 ln -sf pypy-2.3.1-linux64 pypy
 
-fw_get https://bootstrap.pypa.io/get-pip.py
-pypy/bin/pypy get-pip.py
-
-pypy/bin/pip install -r $FWROOT/config/requirements-pypy.txt
+if [ ! -f "get-pip.py" ]; then
+fw_get https://bootstrap.pypa.io/get-pip.py -O get-pip.py
+fi
+./pypy/bin/pypy get-pip.py

+ 8 - 7
toolset/setup/linux/languages/python2.sh

@@ -3,15 +3,16 @@
 RETCODE=$(fw_exists py2)
 [ ! "$RETCODE" == 0 ] || { return 0; }
 
-fw_get http://www.python.org/ftp/python/2.7.7/Python-2.7.7.tgz
-fw_untar Python-2.7.7.tgz
+fw_get http://www.python.org/ftp/python/2.7.8/Python-2.7.8.tgz
+fw_untar Python-2.7.8.tgz
 pre=$(pwd)
-cd Python-2.7.7
+cd Python-2.7.8
 ./configure --prefix=${pre}/py2 --disable-shared
 make -j4
 make install
-
 cd ..
-fw_get https://bootstrap.pypa.io/get-pip.py -O get-pip2.py
-py2/bin/python get-pip2.py
-py2/bin/pip install -r $FWROOT/config/requirements.txt
+
+if [ ! -f "get-pip.py" ]; then
+fw_get https://bootstrap.pypa.io/get-pip.py -O get-pip.py
+fi
+./py2/bin/python get-pip.py

+ 5 - 2
toolset/setup/linux/languages/python3.sh

@@ -10,6 +10,9 @@ cd Python-3.4.1
 ./configure --prefix=${pre}/py3 --disable-shared
 make -j4
 make install
-
 cd ..
-py3/bin/pip3 install -r $FWROOT/config/requirements-py3.txt
+
+if [ ! -f "get-pip.py" ]; then
+fw_get https://bootstrap.pypa.io/get-pip.py -O get-pip.py
+fi
+./py3/bin/python get-pip.py

+ 2 - 2
tornado/bash_profile.sh

@@ -10,7 +10,7 @@ export PYPY_PIP=$PYPY_ROOT/bin/pip
 export PYPY_GUNICORN=$PYPY_ROOT/bin/gunicorn
 
 export PY3_ROOT=$IROOT/py3
-export PY3=$PY3_ROOT/bin/python
-export PY3_PIP=$PY3_ROOT/bin/pip
+export PY3=$PY3_ROOT/bin/python3
+export PY3_PIP=$PY3_ROOT/bin/pip3
 export PY3_GUNICORN=$PY3_ROOT/bin/gunicorn
 

+ 1 - 1
tornado/benchmark_config

@@ -61,7 +61,7 @@
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "tornado-py3",
-      "notes": "CPython 3.3",
+      "notes": "CPython 3.4",
       "versus": ""
     }
   }]

+ 7 - 1
tornado/install.sh

@@ -1,3 +1,9 @@
 #!/bin/bash
 
-fw_depends python2 python3 pypy
+mkdir -p ~/.pip_cache
+export PIP_DOWNLOAD_CACHE=$HOME/.pip_cache
+
+fw_depends python2 python3
+
+$IROOT/py2/bin/pip install -r $TROOT/requirements.txt
+$IROOT/py3/bin/pip3 install -r $TROOT/requirements.txt

+ 3 - 2
tornado/requirements.txt

@@ -1,2 +1,3 @@
-tornado
-motor
+tornado==3.2.2
+motor==0.3.2
+Momoko==1.1.4

+ 2 - 1
tornado/setup.py

@@ -6,7 +6,8 @@ import time
 
 
 def start(args, logfile, errfile):
-    subprocess.Popen("$PY2 server.py --port=8080 --mongo=%s --logging=error" % (args.database_host,),
+    subprocess.Popen(
+        "$PY2 server.py --port=8080 --mongo=%s --logging=error" % (args.database_host,),
         shell=True, cwd='tornado', stderr=errfile, stdout=logfile)
     return 0
 

+ 15 - 9
tornado/setup_py3.py

@@ -1,21 +1,27 @@
-from os.path import expanduser
-from os import kill
+import os
 import subprocess
 import sys
 import time
 
+CWD = os.path.dirname(__file__)
+
 
 def start(args, logfile, errfile):
-    subprocess.Popen("$PY3 server.py --port=8080 --mongo=%s --logging=error" % (args.database_host,),
-        shell=True, cwd='tornado', stderr=errfile, stdout=logfile)
+    subprocess.Popen(
+        "$PY3 server.py --port=8080 --mongo=%s --logging=error" % (args.database_host,),
+        shell=True, cwd=CWD, stderr=errfile, stdout=logfile)
     return 0
 
+
 def stop(logfile, errfile):
-    for line in subprocess.check_output(["ps", "aux"]).splitlines():
-        if 'server.py --port=8080' in line:
-            pid = int(line.split(None,2)[1])
-            kill(pid, 9)
-    return 0
+    subprocess.call(
+        'pgrep -f server.py',
+        shell=True, stderr=errfile, stdout=logfile)
+    subprocess.call(
+        'pkill -f server.py',
+        shell=True, stderr=errfile, stdout=logfile)
+    time.sleep(1)
+
 
 if __name__ == '__main__':
     class DummyArg:

+ 14 - 0
uwsgi/bash_profile.sh

@@ -0,0 +1,14 @@
+export PY2_ROOT=$IROOT/py2
+export PY2=$PY2_ROOT/bin/python
+export PY2_PIP=$PY2_ROOT/bin/pip
+export PY2_GUNICORN=$PY2_ROOT/bin/gunicorn
+
+export PYPY_ROOT=$IROOT/pypy
+export PYPY=$PYPY_ROOT/bin/python
+export PYPY_PIP=$PYPY_ROOT/bin/pip
+export PYPY_GUNICORN=$PYPY_ROOT/bin/gunicorn
+
+export PY3_ROOT=$IROOT/py3
+export PY3=$PY3_ROOT/bin/python
+export PY3_PIP=$PY3_ROOT/bin/pip3
+export PY3_GUNICORN=$PY3_ROOT/bin/gunicorn

+ 5 - 0
uwsgi/install.sh

@@ -1,3 +1,8 @@
 #!/bin/bash
 
+mkdir -p ~/.pip_cache
+export PIP_DOWNLOAD_CACHE=$HOME/.pip_cache
+
 fw_depends python2 nginx
+
+$IROOT/py2/bin/pip install -r $TROOT/requirements.txt

+ 48 - 0
uwsgi/nginx.conf

@@ -0,0 +1,48 @@
+# This file is based on /usr/local/nginx/conf/nginx.conf.default.
+
+# One worker process per core
+error_log stderr error;
+
+events {
+    # This needed to be increased because the nginx error log said so.
+    # http://nginx.org/en/docs/ngx_core_module.html#worker_connections
+    worker_connections  65535;
+    multi_accept on;
+}
+
+http {
+    default_type  application/octet-stream;
+    client_body_temp_path      /tmp;
+
+    # turn off request logging for performance
+    access_log off;
+
+    # I think these only options affect static file serving
+    sendfile        on;
+    tcp_nopush      on;
+
+    # Allow many HTTP Keep-Alive requests in a single TCP connection before
+    # closing it (the default is 100). This will minimize the total number
+    # of TCP connections opened/closed. The problem is that this may cause
+    # some worker processes to be handling too connections relative to the
+    # other workers based on an initial imbalance, so this is disabled for
+    # now.
+#    keepalive_requests 1000;
+
+    #keepalive_timeout  0;
+    keepalive_timeout  65;
+
+    server {
+        # For information on deferred, see:
+        # http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
+        # http://www.techrepublic.com/article/take-advantage-of-tcp-ip-options-to-optimize-data-transmission/
+        # The backlog argument to listen() is set to match net.ipv4.tcp_max_syn_backlog and net.core.somaxconn
+        listen       8080 default_server deferred backlog=65535;
+        server_name  localhost;
+
+        location / {
+            uwsgi_pass unix:/tmp/uwsgi.sock;
+            include /usr/local/nginx/conf/uwsgi_params;
+        }
+    }    
+}

+ 2 - 2
uwsgi/requirements.txt

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

+ 4 - 8
uwsgi/setup.py

@@ -1,13 +1,12 @@
 import multiprocessing
 import subprocess
-import sys
-import setup_util
 import os
 import time
 
-uwsgi = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin/uwsgi')
+uwsgi = os.path.expandvars('$PY2_ROOT/bin/uwsgi')
 PROCS = multiprocessing.cpu_count()
 
+
 def start(args, logfile, errfile):
     # --http and --http-processes create http router processes that process the
     # incoming connections and pass them to the worker processes (-p). We use
@@ -23,9 +22,6 @@ def start(args, logfile, errfile):
 
 
 def stop(logfile, errfile):
-    try:
-        subprocess.Popen(uwsgi + ' --stop /tmp/uwsgi.pid', shell=True, cwd="uwsgi", stderr=errfile, stdout=logfile)
-    except OSError:
-        pass
-    time.sleep(1)
+    subprocess.call(uwsgi + ' --stop /tmp/uwsgi.pid', shell=True, cwd="uwsgi", stderr=errfile, stdout=logfile)
+    os.system('killall uwsgi')
     return 0

+ 10 - 6
uwsgi/setup_nginx.py

@@ -2,10 +2,11 @@ import subprocess
 import multiprocessing
 import os
 
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
-config_dir = os.path.expanduser('~/FrameworkBenchmarks/config')
+
+CWD = os.path.abspath(os.path.dirname(__file__))
+uwsgi = os.path.expandvars('$PY2_ROOT/bin/uwsgi')
 NCPU = multiprocessing.cpu_count()
-NGINX_COMMAND = 'sudo /usr/local/nginx/sbin/nginx -c ' + config_dir + '/nginx_uwsgi.conf'
+NGINX_COMMAND = 'sudo /usr/local/nginx/sbin/nginx -c ' + CWD + '/nginx.conf'
 
 
 def start(args, logfile, errfile):
@@ -16,7 +17,8 @@ def start(args, logfile, errfile):
 
         # Run in the background, but keep stdout/stderr for easy debugging.
         # Note that this uses --gevent 1000 just like setup.py.
-        subprocess.Popen(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi.ini' +
+        subprocess.Popen(
+            uwsgi + ' --ini uwsgi.ini' +
             ' --processes ' + str(NCPU) +
             ' --gevent 1000 --wsgi hello',
             shell=True, cwd='uwsgi',
@@ -26,11 +28,13 @@ def start(args, logfile, errfile):
         return 1
 
 def stop(logfile, errfile):
-    subprocess.check_call(
+    subprocess.call(
         NGINX_COMMAND + ' -s stop',
         shell=True, stdout=logfile, stderr=errfile)
 
-    subprocess.call(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi_stop.ini', shell=True, stdout=logfile, stderr=errfile)
+    subprocess.call(
+        uwsgi + ' --stop /tmp/uwsgi.pid',
+        shell=True, cwd="uwsgi", stderr=errfile, stdout=logfile)
 
     os.system('killall nginx')
     os.system('killall uwsgi')

+ 19 - 0
uwsgi/uwsgi.ini

@@ -0,0 +1,19 @@
+[uwsgi]
+master
+; Increase listen queue used for nginx connecting to uWSGI. This matches
+; net.ipv4.tcp_max_syn_backlog and net.core.somaxconn.
+listen = 65535
+; for performance
+disable-logging
+; use UNIX sockets instead of TCP loopback for performance
+socket = /tmp/uwsgi.sock
+; allow nginx to access the UNIX socket
+chmod-socket = 666
+; Avoid thundering herd problem http://uwsgi-docs.readthedocs.org/en/latest/articles/SerializingAccept.html .
+; This is currently disabled because when I tried it with flask, it caused a
+; 20% performance hit. The CPU cores could not be saturated with thunder-lock.
+; I'm not yet sure the full story, so this is presently disabled. Also,
+; disabling this caused bottle to get ~13% faster.
+;thunder-lock
+; used by uwsgi_stop.ini
+pidfile = /tmp/uwsgi.pid

+ 14 - 0
wsgi/bash_profile.sh

@@ -0,0 +1,14 @@
+export PY2_ROOT=$IROOT/py2
+export PY2=$PY2_ROOT/bin/python
+export PY2_PIP=$PY2_ROOT/bin/pip
+export PY2_GUNICORN=$PY2_ROOT/bin/gunicorn
+
+export PYPY_ROOT=$IROOT/pypy
+export PYPY=$PYPY_ROOT/bin/python
+export PYPY_PIP=$PYPY_ROOT/bin/pip
+export PYPY_GUNICORN=$PYPY_ROOT/bin/gunicorn
+
+export PY3_ROOT=$IROOT/py3
+export PY3=$PY3_ROOT/bin/python
+export PY3_PIP=$PY3_ROOT/bin/pip3
+export PY3_GUNICORN=$PY3_ROOT/bin/gunicorn

+ 22 - 0
wsgi/gunicorn_conf.py

@@ -0,0 +1,22 @@
+import multiprocessing
+import sys
+
+_is_pypy = hasattr(sys, 'pypy_version_info')
+
+# only implements json and plain. Not wait DB.
+workers = multiprocessing.cpu_count()
+bind = "0.0.0.0:8080"
+keepalive = 120
+errorlog = '-'
+pidfile = 'gunicorn.pid'
+
+if _is_pypy:
+    worker_class = "tornado"
+else:
+    worker_class = "meinheld.gmeinheld.MeinheldWorker"
+
+    def post_fork(server, worker):
+        # Disalbe access log
+        import meinheld.server
+        meinheld.server.set_access_logger(None)
+

+ 0 - 8
wsgi/hello.py

@@ -33,11 +33,3 @@ def app(environ, start_response):
         return json(environ, start_response)
     else:
         return plaintext(environ, start_response)
-
-
-try:
-    import meinheld
-    meinheld.server.set_access_logger(None)
-    meinheld.set_keepalive(120)
-except ImportError:
-    pass

+ 5 - 0
wsgi/install.sh

@@ -1,3 +1,8 @@
 #!/bin/bash
 
+mkdir -p ~/.pip_cache
+export PIP_DOWNLOAD_CACHE=$HOME/.pip_cache
+
 fw_depends python2 nginx
+
+$IROOT/py2/bin/pip install -r $TROOT/requirements.txt

+ 48 - 0
wsgi/nginx.conf

@@ -0,0 +1,48 @@
+# This file is based on /usr/local/nginx/conf/nginx.conf.default.
+
+# One worker process per core
+error_log stderr error;
+
+events {
+    # This needed to be increased because the nginx error log said so.
+    # http://nginx.org/en/docs/ngx_core_module.html#worker_connections
+    worker_connections  65535;
+    multi_accept on;
+}
+
+http {
+    default_type  application/octet-stream;
+    client_body_temp_path      /tmp;
+
+    # turn off request logging for performance
+    access_log off;
+
+    # I think these only options affect static file serving
+    sendfile        on;
+    tcp_nopush      on;
+
+    # Allow many HTTP Keep-Alive requests in a single TCP connection before
+    # closing it (the default is 100). This will minimize the total number
+    # of TCP connections opened/closed. The problem is that this may cause
+    # some worker processes to be handling too connections relative to the
+    # other workers based on an initial imbalance, so this is disabled for
+    # now.
+#    keepalive_requests 1000;
+
+    #keepalive_timeout  0;
+    keepalive_timeout  65;
+
+    server {
+        # For information on deferred, see:
+        # http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
+        # http://www.techrepublic.com/article/take-advantage-of-tcp-ip-options-to-optimize-data-transmission/
+        # The backlog argument to listen() is set to match net.ipv4.tcp_max_syn_backlog and net.core.somaxconn
+        listen       8080 default_server deferred backlog=65535;
+        server_name  localhost;
+
+        location / {
+            uwsgi_pass unix:/tmp/uwsgi.sock;
+            include /usr/local/nginx/conf/uwsgi_params;
+        }
+    }    
+}

+ 4 - 3
wsgi/requirements.txt

@@ -1,3 +1,4 @@
-ujson
-circus
-Chaussette
+ujson==1.33
+uwsgi
+gunicorn==19.1
+meinheld==0.5.6

+ 8 - 33
wsgi/setup.py

@@ -1,43 +1,18 @@
 import subprocess
-import setup_util
-import multiprocessing
 import os
 
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
-NCPU = multiprocessing.cpu_count()
-
-CIRCUS_INI = """\
-[watcher:app]
-cmd = {BIN}/chaussette --fd=$(circus.sockets.app) --backend=meinheld hello.app
-use_sockets = True
-numprocesses = {PROCS}
-
-[socket:app]
-host = 0.0.0.0
-port = 8080
-"""
-
-proc = None
+CWD = os.path.dirname(__file__)
 
 
 def start(args, logfile, errfile):
-    global proc
-
-    subprocess.check_call(bin_dir + "/pip install -r requirements.txt",
-                          cwd="wsgi", stderr=errfile, stdout=logfile, shell=True)
-
-    with open("wsgi/circus.ini", "w") as f:
-        f.write(CIRCUS_INI.format(BIN=bin_dir, PROCS=NCPU*2))
-
-    proc = subprocess.Popen([bin_dir + "/circusd", "circus.ini"],
-		            cwd="wsgi", stderr=errfile, stdout=logfile)
+    subprocess.Popen(
+        "$PY2_GUNICORN hello:app -c gunicorn_conf.py",
+        cwd=CWD, stderr=errfile, stdout=logfile, shell=True)
     return 0
 
+
 def stop(logfile, errfile):
-    global proc
-    if proc is None:
-        return 0
-    proc.terminate()
-    proc.wait()
-    proc = None
+    subprocess.call(
+        "kill `cat gunicorn.pid`",
+        cwd=CWD, stderr=errfile, stdout=logfile, shell=True)
     return 0

+ 11 - 9
wsgi/setup_nginxuwsgi.py

@@ -2,10 +2,12 @@ import subprocess
 import multiprocessing
 import os
 
-bin_dir = os.path.expanduser('~/FrameworkBenchmarks/installs/py2/bin')
-config_dir = os.path.expanduser('~/FrameworkBenchmarks/config')
+
+CWD = os.path.abspath(os.path.dirname(__file__))
+bin_dir = os.path.expandvars('$PY2_ROOT/bin')
 NCPU = multiprocessing.cpu_count()
-NGINX_COMMAND = 'sudo /usr/local/nginx/sbin/nginx -c ' + config_dir + '/nginx_uwsgi.conf'
+NGINX_COMMAND = 'sudo /usr/local/nginx/sbin/nginx -c ' + CWD + '/nginx.conf'
+
 
 def start(args, logfile, errfile):
     try:
@@ -14,10 +16,11 @@ def start(args, logfile, errfile):
             shell=True, stdout=logfile, stderr=errfile)
 
         # Run in the background, but keep stdout/stderr for easy debugging
-        subprocess.Popen(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi.ini' +
+        subprocess.Popen(
+            bin_dir + '/uwsgi --ini uwsgi.ini' +
             ' --processes ' + str(NCPU) +
             ' --wsgi hello:app',
-            shell=True, cwd='wsgi', stdout=logfile, stderr=errfile)
+            shell=True, cwd=CWD, stdout=logfile, stderr=errfile)
         return 0
     except subprocess.CalledProcessError:
         return 1
@@ -27,8 +30,7 @@ def stop(logfile, errfile):
         NGINX_COMMAND + ' -s stop',
         shell=True, stdout=logfile, stderr=errfile)
 
-    subprocess.call(bin_dir + '/uwsgi --ini ' + config_dir + '/uwsgi_stop.ini', shell=True, stdout=logfile, stderr=errfile)
-
-    os.system('killall nginx')
-    os.system('killall uwsgi')
+    subprocess.call(
+        bin_dir + '/uwsgi --stop /tmp/uwsgi.pid',
+        shell=True, cwd=CWD, stderr=errfile, stdout=logfile)
     return 0

+ 19 - 0
wsgi/uwsgi.ini

@@ -0,0 +1,19 @@
+[uwsgi]
+master
+; Increase listen queue used for nginx connecting to uWSGI. This matches
+; net.ipv4.tcp_max_syn_backlog and net.core.somaxconn.
+listen = 65535
+; for performance
+disable-logging
+; use UNIX sockets instead of TCP loopback for performance
+socket = /tmp/uwsgi.sock
+; allow nginx to access the UNIX socket
+chmod-socket = 666
+; Avoid thundering herd problem http://uwsgi-docs.readthedocs.org/en/latest/articles/SerializingAccept.html .
+; This is currently disabled because when I tried it with flask, it caused a
+; 20% performance hit. The CPU cores could not be saturated with thunder-lock.
+; I'm not yet sure the full story, so this is presently disabled. Also,
+; disabling this caused bottle to get ~13% faster.
+;thunder-lock
+; used by uwsgi_stop.ini
+pidfile = /tmp/uwsgi.pid