Commits

Andriy Kornatskyy committed 593e76c

Added caching benchmark for django and wheezy.web.

Comments (0)

Files changed (17)

07-caching/Makefile

+.SILENT: clean env pypy django flask pyramid web.py bottle wheezy.web tornado web2py bobo cherrypy wsgi circuits
+.PHONY: clean env pypy django flask pyramid web.py bottle wheezy.web tornado web2py bobo cherrypy wsgi circuits
+
+VERSION=2.7
+PYPI=http://pypi.python.org/simple
+ENV=env
+
+PYTHON=$(ENV)/bin/python$(VERSION)
+PYPY=pypy-1.9
+
+SERVER=uwsgi
+
+
+env:
+	PYTHON_EXE=/usr/local/bin/python$(VERSION); \
+	if [ ! -x $$PYTHON_EXE ]; then \
+		    PYTHON_EXE=/usr/bin/python$(VERSION); \
+	fi; \
+	virtualenv --python=$$PYTHON_EXE --no-site-packages env
+
+	if [ "$$(echo $(VERSION) | sed 's/\.//')" -lt 30 ]; then \
+		cd $(ENV)/bin && ./easy_install-$(VERSION) -i $(PYPI) -O2 \
+			"uwsgi>=1.2.6" "gunicorn>=0.14.6" "django>=1.4.1" \
+			"wheezy.web>=0.1.304" ; \
+	else \
+		cd $(ENV)/bin && ./easy_install-$(VERSION) -i $(PYPI) \
+				"distribute>=0.6.28" \
+			&& ./easy_install-$(VERSION) -i $(PYPI) -O2 \
+		   		"uwsgi>=1.3" "wheezy.web>=0.1.304" ; \
+	fi
+
+pypy:
+	if [ `uname -m` = "x86_64" ]; then \
+		ARCH='64'; \
+	else \
+		ARCH=''; \
+	fi; \
+	if [ ! -f $(PYPY)-linux$$ARCH.tar.bz2 ]; then \
+		wget https://bitbucket.org/pypy/pypy/downloads/$(PYPY)-linux$$ARCH.tar.bz2; \
+	fi; \
+	tar xjf $(PYPY)-linux$$ARCH.tar.bz2; \
+	wget http://python-distribute.org/distribute_setup.py ; \
+	$(PYPY)/bin/pypy distribute_setup.py ; \
+	rm distribute* ; \
+	cd $(PYPY)/bin && ./easy_install -i $(PYPI) -O2 \
+		"gunicorn>=0.14.6" "wheezy.web>=0.1.304"
+
+clean:
+	find ./ -type d -name __pycache__ | xargs rm -rf
+	find ./ -name '*.py[co]' -delete
+
+django:
+ifeq ($(SERVER),uwsgi)
+	$(ENV)/bin/uwsgi --ini django/uwsgi.ini
+else
+	export PYTHONPATH=$$PYTHONPATH:django; \
+	$(ENV)/bin/gunicorn -b 0.0.0.0:8080 -w 4 helloworld.wsgi:application
+endif
+
+wheezy.web:
+ifeq ($(SERVER),uwsgi)
+	$(ENV)/bin/uwsgi --ini wheezy.web/uwsgi.ini
+else
+	export PYTHONPATH=$$PYTHONPATH:wheezy.web ; \
+	$(ENV)/bin/gunicorn -b 0.0.0.0:8080 -w 4 app:main
+endif

07-caching/benchmark.py

+
+"""
+"""
+
+import os
+import sys
+
+try:
+    import cProfile as profile
+except ImportError:
+    import profile
+
+from pstats import Stats
+from timeit import timeit
+
+from samples import environ
+
+
+path = os.getcwd()
+
+frameworks = ['wheezy.web']
+frameworks += ['django']
+frameworks = sorted(frameworks)
+
+
+def start_response(status, headers):
+    return None
+
+
+def run(name, wrapper, number=10000):
+    sys.path[0] = '.'
+    print("\n%-11s   msec    rps  tcalls  funcs" % name)
+    for framework in frameworks:
+        os.chdir(os.path.join(path, framework))
+        try:
+            main = __import__('app', None, None, ['main']).main
+            f = lambda: wrapper(main)
+            time = timeit(f, number=number)
+            st = Stats(profile.Profile().runctx('f()', globals(), locals()))
+            print("%-11s %6.0f %6.0f %7d %6d" % (framework, 1000 * time,
+                  number / time, st.total_calls, len(st.stats)))
+            if 0:
+                st = Stats(profile.Profile().runctx(
+                    'timeit(f, number=number)', globals(), locals()))
+                st.strip_dirs().sort_stats('time').print_stats(10)
+            del sys.modules['app']
+        except ImportError:
+            print("%-15s not installed" % framework)
+
+
+def build_wrapper(path_info):
+    def wrapper(main):
+        e = environ.copy()
+        e['PATH_INFO'] = e['REQUEST_URI'] = path_info
+        list(main(e, start_response))
+    return wrapper
+
+
+if __name__ == '__main__':
+    run('welcome', build_wrapper('/welcome'))
+    run('memory', build_wrapper('/memory'))
+    run('pylibmc', build_wrapper('/pylibmc'))
+    run('memcache', build_wrapper('/memcache'))

07-caching/django/app.py

+
+from helloworld.wsgi import application as main

07-caching/django/helloworld/500.html

Empty file added.

07-caching/django/helloworld/__init__.py

Empty file added.

07-caching/django/helloworld/settings.py

+# Django settings for helloworld project.
+
+import os
+
+DEBUG = False
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    # ('Your Name', 'your_email@example.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
+        'NAME': '',                      # Or path to database file if using sqlite3.
+        'USER': '',                      # Not used with sqlite3.
+        'PASSWORD': '',                  # Not used with sqlite3.
+        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
+        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
+    }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# In a Windows environment this must be set to your system time zone.
+TIME_ZONE = 'America/Chicago'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale.
+USE_L10N = True
+
+# If you set this to False, Django will not use timezone-aware datetimes.
+USE_TZ = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/media/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash.
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
+MEDIA_URL = ''
+
+# Absolute path to the directory static files should be collected to.
+# Don't put anything in this directory yourself; store your static files
+# in apps' "static/" subdirectories and in STATICFILES_DIRS.
+# Example: "/home/media/media.lawrence.com/static/"
+STATIC_ROOT = ''
+
+# URL prefix for static files.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = '/static/'
+
+# Additional locations of static files
+STATICFILES_DIRS = (
+    # Put strings here, like "/home/html/static" or "C:/www/django/static".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+# List of finder classes that know how to find static files in
+# various locations.
+STATICFILES_FINDERS = (
+    'django.contrib.staticfiles.finders.FileSystemFinder',
+    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
+)
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = '85qzvlv^w(%s@%5yt+^==jwrxse_8id+gl2gn+%)h@u-jz+n73'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+#     'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+    #'django.middleware.common.CommonMiddleware',
+    #'django.contrib.sessions.middleware.SessionMiddleware',
+    #'django.middleware.csrf.CsrfViewMiddleware',
+    #'django.contrib.auth.middleware.AuthenticationMiddleware',
+    #'django.contrib.messages.middleware.MessageMiddleware',
+    # Uncomment the next line for simple clickjacking protection:
+    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+)
+
+ROOT_URLCONF = 'helloworld.urls'
+
+# Python dotted path to the WSGI application used by Django's runserver.
+WSGI_APPLICATION = 'helloworld.wsgi.application'
+
+ROOTDIR = os.path.abspath(os.path.dirname(__file__))
+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.
+    ROOTDIR + '/',
+)
+
+INSTALLED_APPS = (
+    #'django.contrib.auth',
+    #'django.contrib.contenttypes',
+    #'django.contrib.sessions',
+    #'django.contrib.sites',
+    #'django.contrib.messages',
+    #'django.contrib.staticfiles',
+    # Uncomment the next line to enable the admin:
+    # 'django.contrib.admin',
+    # Uncomment the next line to enable admin documentation:
+    # 'django.contrib.admindocs',
+)
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error when DEBUG=False.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': True,
+    'filters': {
+        'require_debug_false': {
+            '()': 'django.utils.log.RequireDebugFalse'
+        }
+    },
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'filters': ['require_debug_false'],
+            'class': 'django.utils.log.AdminEmailHandler'
+        }
+    },
+    'loggers': {
+        'django.request': {
+            'handlers': ['mail_admins'],
+            'level': 'ERROR',
+            'propagate': True,
+        },
+    }
+}
+
+CACHES = {
+    'default': {
+        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'
+    },
+    'memory': {
+        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'
+    },
+    'memcache': {
+        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
+        'LOCATION': 'unix:/tmp/memcached.sock',
+    },
+    'pylibmc': {
+        'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
+        'LOCATION': '/tmp/memcached.sock',
+    }
+}

07-caching/django/helloworld/urls.py

+from django.conf.urls import patterns, include, url
+
+# Uncomment the next two lines to enable the admin:
+# from django.contrib import admin
+# admin.autodiscover()
+
+urlpatterns = patterns(
+    '',
+    url(r'^welcome$', 'helloworld.views.welcome', name='welcome'),
+    url(r'^memory$', 'helloworld.views.memory', name='memory'),
+    url(r'^pylibmc$', 'helloworld.views.pylibmc', name='pylibmc'),
+    url(r'^memcache$', 'helloworld.views.memcache', name='memcache'),
+    # Examples:
+    # url(r'^$', 'helloworld.views.home', name='home'),
+    # url(r'^helloworld/', include('helloworld.foo.urls')),
+
+    # Uncomment the admin/doc line below to enable admin documentation:
+    # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
+    # Uncomment the next line to enable the admin:
+    # url(r'^admin/', include(admin.site.urls)),
+)

07-caching/django/helloworld/views.py

+
+from django.http import HttpResponse
+from django.views.decorators.cache import cache_page
+
+hello = 'Hello World!' * 350
+
+
+def welcome(request):
+    return HttpResponse(hello)
+
+
+@cache_page(60 * 15, cache="memory")
+def memory(request):
+    return HttpResponse(hello)
+
+
+@cache_page(60 * 15, cache="pylibmc")
+def pylibmc(request):
+    return HttpResponse(hello)
+
+
+@cache_page(60 * 15, cache="memcache")
+def memcache(request):
+    return HttpResponse(hello)

07-caching/django/helloworld/wsgi.py

+"""
+WSGI config for helloworld project.
+
+This module contains the WSGI application used by Django's development server
+and any production WSGI deployments. It should expose a module-level variable
+named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
+this application via the ``WSGI_APPLICATION`` setting.
+
+Usually you will have the standard Django WSGI application here, but it also
+might make sense to replace the whole Django WSGI application with a custom one
+that later delegates to the Django one. For example, you could introduce WSGI
+middleware here, or combine a Django application with an application of another
+framework.
+
+"""
+import os
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "helloworld.settings")
+
+# This application object is used by any WSGI server configured to use this
+# file. This includes Django's development server, if the WSGI_APPLICATION
+# setting points here.
+from django.core.wsgi import get_wsgi_application
+application = get_wsgi_application()
+
+# Apply WSGI middleware here.
+# from helloworld.wsgi import HelloWorldApplication
+# application = HelloWorldApplication(application)

07-caching/django/manage.py

+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == "__main__":
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "helloworld.settings")
+
+    from django.core.management import execute_from_command_line
+
+    execute_from_command_line(sys.argv)

07-caching/django/uwsgi.ini

+
+[uwsgi]
+chdir = django
+virtualenv = ../env
+pythonpath = .
+module = helloworld.wsgi
+procname-prefix = MyDjango-
+
+http-socket = 0.0.0.0:8080
+listen = 1024
+uid = www-data
+gid = www-data
+harakiri = 10
+optimize = 2
+master = True
+processes = 4
+disable-logging = True
+#logto = /dev/null
+no-default-app = False
+auto-procname = True
+limit-as = 70
+buffer-size = 2048
+post-buffering = 1024
+limit-post = 1024
+thread-stacksize = 64

07-caching/samples.py

+
+environ = {
+    'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;'
+                   'q=0.9,*/*;q=0.8',
+    'HTTP_ACCEPT_CHARSET': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
+    'HTTP_ACCEPT_ENCODING': 'gzip,deflate,sdch',
+    'HTTP_ACCEPT_LANGUAGE': 'uk,en-US;q=0.8,en;q=0.6',
+    'HTTP_CONNECTION': 'keep-alive',
+    'HTTP_HOST': 'vm0.dev.local:8080',
+    'HTTP_USER_AGENT': 'Mozilla/5.0 (X11; Linux i686)',
+    'QUERY_STRING': '',
+    'REMOTE_ADDR': '127.0.0.1',
+    'REQUEST_METHOD': 'GET',
+    'SCRIPT_NAME': '',
+    'SERVER_NAME': 'localhost',
+    'SERVER_PORT': '8080',
+    'SERVER_PROTOCOL': 'HTTP/1.1',
+    'uwsgi.node': 'localhost',
+    'uwsgi.version': '1.2.6',
+    'wsgi.errors': None,
+    'wsgi.file_wrapper': None,
+    'wsgi.input': None,
+    'wsgi.multiprocess': False,
+    'wsgi.multithread': False,
+    'wsgi.run_once': False,
+    'wsgi.url_scheme': 'http',
+    'wsgi.version': (1, 0),
+}

07-caching/wheezy.web/app.py

+import warnings
+
+from wheezy.http import WSGIApplication
+from wheezy.http.middleware import http_cache_middleware_factory
+from wheezy.web.middleware import bootstrap_defaults
+from wheezy.web.middleware import path_routing_middleware_factory
+
+from config import cache_factory
+from urls import all_urls
+
+
+options = {}
+
+# HTTPCacheMiddleware
+options.update({
+    'http_cache_factory': cache_factory
+})
+
+warnings.simplefilter('ignore')
+main = WSGIApplication(
+    middleware=[
+        bootstrap_defaults(url_mapping=all_urls),
+        http_cache_middleware_factory,
+        path_routing_middleware_factory
+    ],
+    options=options
+)

07-caching/wheezy.web/config.py

+
+from datetime import timedelta
+
+from wheezy.caching import CacheClient
+from wheezy.caching import MemoryCache
+from wheezy.caching.memcache import client_factory as memcache_factory
+from wheezy.caching.pools import EagerPool
+from wheezy.caching.pools import Pooled
+from wheezy.caching.pylibmc import client_factory as pylibmc_factory
+from wheezy.http import CacheProfile
+
+
+# cache config
+
+memory_cache = MemoryCache()
+
+memcache_cache = memcache_factory(['unix:/tmp/memcached.sock'])
+
+pylibmc_pool = EagerPool(lambda: pylibmc_factory(['/tmp/memcached.sock']),
+                         size=100)
+
+cache = CacheClient({
+    'memory': lambda: memory_cache,
+    'memcache': lambda: memcache_cache,
+    'pylibmc': lambda: Pooled(pylibmc_pool)
+}, default_namespace='memory')
+
+cache_factory = lambda: cache
+
+# cache profiles
+
+memory_profile = CacheProfile(
+    'server', namespace='memory', duration=timedelta(minutes=15))
+memcache_profile = CacheProfile(
+    'server', namespace='memcache', duration=timedelta(minutes=15))
+pylibmc_profile = CacheProfile(
+    'server', namespace='pylibmc', duration=timedelta(minutes=15))

07-caching/wheezy.web/urls.py

+
+import views
+
+from wheezy.routing import url
+
+
+all_urls = [
+    url('welcome', views.WelcomeHandler),
+    url('memory', views.MemoryHandler),
+    url('pylibmc', views.PylibmcHandler),
+    url('memcache', views.MemcacheHandler),
+]

07-caching/wheezy.web/uwsgi.ini

+
+[uwsgi]
+chdir = wheezy.web
+virtualenv = ../env
+pythonpath = .
+wsgi = app:main
+procname-prefix = MyWheezyWeb-
+
+http-socket = 0.0.0.0:8080
+listen = 1024
+uid = www-data
+gid = www-data
+harakiri = 10
+optimize = 2
+master = True
+processes = 4
+disable-logging = True
+#logto = /dev/null
+no-default-app = False
+auto-procname = True
+limit-as = 90
+buffer-size = 2048
+post-buffering = 1024
+limit-post = 1024
+thread-stacksize = 64

07-caching/wheezy.web/views.py

+
+import config
+
+from wheezy.http import HTTPResponse
+from wheezy.web import handler_cache
+from wheezy.web.handlers import BaseHandler
+
+hello = 'Hello World!' * 350
+
+
+class WelcomeHandler(BaseHandler):
+
+    def get(self):
+        response = HTTPResponse()
+        response.write(hello)
+        return response
+
+
+class MemoryHandler(BaseHandler):
+
+    @handler_cache(config.memory_profile)
+    def get(self):
+        response = HTTPResponse()
+        response.write(hello)
+        return response
+
+
+class PylibmcHandler(BaseHandler):
+
+    @handler_cache(config.pylibmc_profile)
+    def get(self):
+        response = HTTPResponse()
+        response.write(hello)
+        return response
+
+
+class MemcacheHandler(BaseHandler):
+
+    @handler_cache(config.memcache_profile)
+    def get(self):
+        response = HTTPResponse()
+        response.write(hello)
+        return response