Архив рубрики: Python

От Python к Scala и C++

Благодаря PyPy мой код стал выполняться в 1.7 раз быстрее, но этого мне было не достаточно. Дальше я решил попробовать переписать парсер на Scala. Скала имеет статическую типизацию и компилируется в Явовский байт-код. Посмотрев тесты производительности я узнал, что Scala в 10 раз быстрее Питона и совсем чуть-чуть медленее чем C++. Конечно в это не очень верилось, но и с плюсами копаться не хотелось.

И вот я переписал код на Scala.

Python 7 мин 40 сек
PyPy 3 ммн 58 сек
Scala 4 мин 20 сек

Результаты скажем прямо меня не порадовали. Скала конечно быстрее CPython, но ведь это даже медленнее чем PyPy. Как вообще такое может быть, язык с динамической типизацией делает, язык со статической типизацией.

Вобщем я был в печали, но делать оставалось не чего, надо было выжимать максимум и я взял в руки С++.
Результат.
C++ 47 сек.

Вот это да! Вот это я понимаю, это реально быстро. 1,5Гб — файл разбирается за 47 секунд.

Однако копание в плюсах навело меня на следующую мысль. В плюсах я не использовал никакие высокоуровневые штуки для парсинга XML, лишь обычные строковые операции. А в Скале какую-то штуку для парсинга XML, а в Питоне регулярки. Получается тест не совсем честный, т.к. алгоритмы разные.

Я вернулся в Скалу и переписал код оставив только стандартные операции со строками.
И результат просто охренителен:
Scala 57 сек

Действительно почти также быстро как в плюсах.

Может реально вся проблема в алгоритме?

После этого я уже не мог не вернуться к Питону, чтобы выкинуть отуда все регулярки и узнать насколько же быстро может он парсить файлы.
Результаты:
CPython 4 мин 12 сек
PyPy 2 мин 48 сек

Да это в два раза лучше начальных результатов, но успех не такой громкий как в Скале.

Окончательные результаты:

CPython 4 мин 12 сек
PyPy 2 мин 48 сек
Scala 57 сек
C++ 47 сек.

Победитель — С++. А кто бы сомневался. Однако Scala настолько близка к плюсам по скорости, что это просто увидительно. Я еще окончательно не решил на каком языке теперь уже полностью перепишу парсер, пока склонаюсь к Scala.

Ускоряем Python — опыт применения PyPy

На одном из проектов уперся в производительность Питона. Такое бывает не часто, но бывает. Сначала думал о том, чтобы переписать код на более шустром языке — C/C++/Scala. Думал-думал, тем временем вспомнил о проекте PyPy(если кто не знает — это реализация Питона на Питоне). На сайте проекта есть тесты производительности, которые показывают что некоторые программы под PyPy начинают работать чуть ли не в 20 раз быстрее. Хотя в среднем ускорение куда меньше. Пишут что в среднем проекты ускоряются в 2 раза. Настроить PyPy куда проще чем переписать код на другом языке.

Очень подробная инструкция настройки PyPy находится здесь: http://doc.pypy.org/en/latest/getting-started.html

Все нужные пакеты установились без проблем — flask, pymongo, pytils, markupsafe, flexmock, nose.
Единственно что не смог установить это lxml, но он здесь мне и не был нужен.

Коненчно я был уверен, что теперь программа будет работать быстрее, оставалось узнать — на сколько быстрее. Результаты такие:
CPython — 5 часов.
PyPy — 3 часа.

Такоми образом ускорение получилось в 1,7 раз. Вот такие результаты. Хорошо это или плохо — зависит от проекта. В моем случае этого не достаточно, и я продолжил искать другие решения.

Запуск Django через Apache и mod_wsgi в Debian / Ubuntu.

Предполагаем apache2 уже установлен, а мы хотим запустить под ним Django. А еще нам нужно чтобы наш проект использовал vitrualenv.

Существует несколько популярных модулей для апача, через которые можно запускать питоновский код: mod_wsgi, mod_fastcgi, mod_fcgi и mod_python. Последний уже много лет как не поддерживается, и имеет много других недостатков. Разница между первыми тремя для нашей цели не принципиальна. Я в данном случае я выбрал mod_wsgi.

Прежде всего нужно установить mod_wsgi.

sudo apt-get install libapache2-mod-wsgi

После чего неплохо бы перезапустить Apache.

sudo /etc/init.d/apache2 restart

В домашней директории сайта (на хостингах это обычно папка www) нужно создать два файла .htaccess и django.wsgi со следующим содержимым.

.htaccess

AddHandler wsgi-script .wsgi
Options +ExecCGI
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ django.wsgi/$1 [QSA,PT,L]

django.wsgi

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, os

# если вы не используете virtualenv, то следующие три строки не нужны
virtual_env = '/home/user/example.com/python/' 
activate_this = os.path.join(virtual_env, 'bin/activate_this.py')
execfile(activate_this, dict(__file__=activate_this))

sys.path.insert(0, '/home/user/example.com')
sys.path.insert(0, '/home/user/example.com/myproject')

os.environ['DJANGO_SETTINGS_MODULE'] = 'myproject.settings'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

Во втором файле поправьте пути и название проекта на свои. После этого все должно работать.

Файл django.wsgi по сути — это обычный спкипт на питоне, из этого следует две вещи:

1. У него должны быть права на исполнение:

chmod +x django.wsgi

2. Чтобы убедиться, что файл работает можно его запустить:

./django.wsgi

Еще замечу, что более правильно расположить django.wsgi в папке cgi-bin (в этом случае подправьте путь в .htaccess).

Собственно все должно работать, если нет смотрите логи апача.

А теперь немножко магии. Каждый раз перезагружать апач при изменении кода проекта это не дело. Есть более удобный вариант.

touch django.wsgi

Правда работать это чудо будет только если наше приложение запущено в режиме демона. Для этого надо сделать следующее.

Открываем конфиг виртуального хоста /etc/apache2/sites-available/example.com и вписываем туда две строки:

WSGIDaemonProcess app processes=2 threads=4
WSGIProcessGroup app

app — название приложения, остальные параметы смотрите в документации.

С этими строками конфиг виртуального хоста будет выглядеть как-то так:

    ServerName example.com
    ServerAlias www.example.com
    ...
    WSGIDaemonProcess app processes=2 threads=4
    WSGIProcessGroup app

Пожалуй все. Удачного деплоя!

Собираем питоновский пакет и выкладываем на pypi

Питоновские пакеты отличный способ повторного использования кода. Если один и тот же код вы уже в третий раз копируете в очередной проект, самое время вынести этот код в отдельный модуль. А еще лучше будет собрать питоновский пакет, который потом будет устанавливаться одной командой.

Например если мы выложили пакет на github, то установить его можно так (django-cleanup в данном случае пример названия пакета):

pip install -e git+git://github.com/un1t/django-cleanup.git#egg=django-cleanup

А если мы залили его на pypi.python.org, то установка будет еще проще:

pip install django-cleanup

Структура пакета

Рассмотрим структуру простого пакета на примере django-cleanup.

django_cleanup/  # папка с модулем
    __init__.py
    models.py
MANIFEST.in 
README.markdown
setup.py

setup.py — отвечает за сборку и установку пакета. Обязательные поля — name, version и url. Можно смело копировать пример в свой пакет, заменив нужные значения. Подробнее об этом файле можно прочитать в документации.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from setuptools import setup, find_packages

setup(
    name     = 'django-cleanup',
    version  = '0.1.5',
    packages = find_packages(),
    requires = ['python (>= 2.5)', 'django (>= 1.3)'],
    description  = 'Deletes old files.',
    long_description = open('README.markdown').read(), 
    author       = 'Ilya Shalyapin',
    author_email = 'ishalyapin@gmail.com',
    url          = 'https://github.com/un1t/django-cleanup',
    download_url = 'https://github.com/un1t/django-cleanup/tarball/master',
    license      = 'MIT License',
    keywords     = 'django',
    classifiers  = [
        'Environment :: Web Environment',
        'Framework :: Django',
        'Intended Audience :: Developers',
        'Programming Language :: Python',
    ],
)

MANIFEST.in — показывает какие дополнительно файлы должны быть включены в дистрибутив, в нашем случае это README.markdown.

include README.markdown

README.markdown — надо сказать что в питоне принято писать ридми в формате RST, т.е. в идеале должно быть README.rst, но мне markdown как-то привычней.

Проверка пакета

После того как создали вышеуказанню структуру пакета неплохо бы проверить, будет ли вообще этот пакет устанавливаться. Ведь можно допустить элементарные синтаксические или другие ошибки. Например все это у нас будет храниться в папке /home/ilya/workspace/django-cleanup/
Соотвественно мы можем установить наш пакет так

pip install -e /home/ilya/workspace/django-cleanup/

и удалить

pip uninstall django-cleanup

Загрузка на PYPI

Если у вас еще нет учетной записи на pypi.python.org, вам нужно зарегистрироваться.

Регистрируем пакет:

python setup.py register

Собираем дистрибутив и загружаем его:

python setup.py sdist upload

Если все прошло гладко то, после этого уже можно ставить свой пакет через pip или easy_install.

Если что-то не сработало или пошло не так, берите любой пакет с того же pypi и смотрите как он устроен.

Ссылки по теме

1. Writing the Setup Script
2. Getting Started With setuptools and setup.py
3. Uploading Packages to the Package Index