Содержание статьи
На самом деле Python — это далеко не один конкретный интерпретатор. Действительно, чаще всего мы имеем дело с так называемым CPython, который можно назвать эталонной реализацией (reference implementation) — интерпретатором, на который все остальные должны равняться. Это означает, что CPython максимально полно и быстро реализует те вещи, которые уже прописаны и добавляются с течением времени в стандарт языка, содержащийся во всякого рода спеках и PEP’ах. И именно эта реализация находится под пристальным вниманием «великодушного пожизненного диктатора» (это реально существующий термин, не веришь — глянь в Википедии) и создателя языка Гвидо ван Россума.
Но все сказанное вовсе не значит, что нельзя просто так взять и воплотить свою собственную реализацию описанных стандартов, равно как ничто не мешает написать, к примеру, свой компилятор для C++. Собственно, довольно большое число разработчиков именно так и сделали. О том, что у них получилось, я и хочу рассказать.
Чтобы понять Python, надо понять Python
Одна из самых известных альтернативных реализаций Python — это PyPy (бывший Psyco), который часто называют питоном, написанным на питоне. У всех, кто слышит подобное определение, возникает закономерный вопрос — как то, что написано на том же языке, может быть быстрее, чем сам язык? Но мы выше уже сошлись на том, что Python — это общее название группы стандартов, а не конкретной реализации. В случае с PyPy мы имеем дело не с CPython, а с так называемым RPython — это даже не совсем диалект языка, это, скорее, платформа или фреймворк для написания своих собственных интерпретаторов.
info
В столкновениях поклонников лагерей Python и Ruby один из аргументов питонистов — это то, что разработчики, которых не устраивала скорость работы языка Ruby, написали якобы более быструю реализацию на RPython. Получился вполне интересный проект под названием Topaz.
RPython привносит немного низкоуровневой магии, которая позволяет при правильном подборе ингредиентов (прямые руки обязательны, глаз тритона — нет) добавить разные полезные плюшки в произвольный интерпретируемый язык (необязательно Python) — например, ускорение за счет JIT. Если хочется больше подробностей об этой штуке, а также если ты сейчас подумал что‑то вроде «а как же LLVM?» — добро пожаловать в FAQ.
Общая мысль заключается в том, что RPython — слишком специфическая реализация для того, чтобы прямо на нем писать боевые программы. Проще и удобнее взять PyPy, даром что он полностью совместим с CPython 2.7 и 3.3 в плане поддерживаемых стандартов. Правда, в силу специфики внутреннего устройства на нем пока что трудновато заставить работать те библиотеки для эталонной реализации, которые используют компилируемые си‑модули. Но, к примеру, Django уже вполне поддерживается, причем во многих случаях бегает быстрее, чем на CPython.
Еще в качестве одной из полезных особенностей PyPy можно назвать его модульность и расширяемость. Например, в него встроена поддержка sandboxing’а — можно запускать «опасный» произвольный код внутри виртуального окружения, даже с эмуляцией файловой системы и запретом на внешние вызовы вроде создания сокетов. А еще в последнее время довольно активно развивается проект PyPy-STM, который позволяет заменить встроенную поддержку многопоточности (ту самую, с GIL и прочими нелюбимыми вещами) на реализацию, работающую через Software Transactional Memory, — то есть с абсолютно другим механизмом разруливания конкурентного доступа.
info
Это не совсем относится к теме статьи, но если тебя заинтересовала информация об RPython — крайне рекомендую взглянуть еще на один проект по «более скоростному запуску» Python. Он называется Nuitka и позиционирует себя как «компилятор Python в C++». Правда, любопытно?
Нижний уровень
Кроме RPython + PyPy, есть и более простой способ ускорить выполнение кода на Python — выбрать один из оптимизирующих компиляторов, который расширяет стандарты языка, добавляя более строгую статическую типизацию и прочие низкоуровневые вещи. Как я выше упомянул, RPython (даже притом, что названное определение к нему тоже относится) является слишком специфичным инструментом для конкретных задач, но есть проект и более общего применения — cython.
Его подход заключается в добавлении фактически нового языка, являющегося чем‑то промежуточным между C и Python (за основу был взят проект Pyrex, который практически не развивается с 2010 года). Обычно исходные файлы с кодом на этом языке имеют расширение pyx, а при натравлении на них компилятора превращаются во вполне обычные C-модули, которые можно тут же импортировать и использовать.
Код с декларацией типов будет выглядеть как‑то так:
def primes(int kmax): cdef int n, k, i cdef int p[1000] result = [] if kmax > 1000: kmax = 1000 k = 0 n = 2 while k < kmax: i = 0 while i < k and n % p[i] != 0: i = i + 1 if i == k: p[k] = n k = k + 1 result.append(n) n = n + 1 return result
В простейшем варианте для такого модуля пишется setup.py примерно с таким содержанием:
from distutils.core import setupfrom Cython.Build import cythonizesetup( ext_modules=cythonize("myscript.pyx"),)
И да, это фактически все, больше ничего в общем случае делать не надо.
Кстати, если кто‑то сейчас начнет возмущаться, что все типы давно уже есть в Numpy и не надо придумывать новый язык, — рекомендую почитать вот эту ссылку. Суть в том, что разработчики проекта активно работают над совместным использованием и того и другого, что позволяет на некоторых задачах получить довольно серьезный прирост производительности.
Змея в коробке
Так уж вышло, что упомянутый автор и BDFL оригинальной реализации Python Гвидо ван Россум сейчас работает в Dropbox, где целая команда под его началом трудится над свежим высокоскоростным интерпретатором Python, который на сей раз называется Pyston. Помимо тысячной версии искажения названия нежно нами любимого языка, он может похвастаться тем, что работает на LLVM с использованием самых современных течений в реализации JIT-компиляции.
Пока что эта реализация находится довольно в зачаточном состоянии, но, по слухам, вполне себе используется для конкретных внутренних проектов в Dropbox, а также, по уверениям разработчиков, должна сделать серьезный шаг вперед с точки зрения производительности (даже по сравнению с CPython). Но главный интерес она может представлять в том, что это своеобразный «лабораторный полигон для испытаний», на котором разработчики играют с различными технологиями — например, там в качестве подключаемого плагина можно использовать альтернативу GIL под названием GRWL или Global Read-Write Lock, которая якобы решает часть старых добрых проблем своего прародителя. Суть в том, что, в отличие от блокирования всего и вся (то есть, грубо говоря, обычного мьютекса, как оно устроено в GIL), он вводит разделение захватных операций на чтение и запись, что позволяет‑таки нескольким потокам получать одновременный доступ к данным, не портя их. Еще можно упомянуть так называемый OSR — On-Stack Replacement, который является своеобразной внутренней магией для запуска «тяжелых» методов (похожая штука используется в JavaScript). Такие дела.
Виртуальная реальность
Если ты продрался через предыдущий раздел, то наверняка уже пришел к мысли о том, что самый действенный способ запилить новый интерпретатор Python — это сделать прослойку поверх существующей виртуальной машины или среды исполнения. Примерно так дела и обстоят: две самые развитые альтернативные реализации (за вычетом упомянутого PyPy) — это проекты для JVM и .NET/Mono.
Jython
Jython — не путать с JPython, похожим проектом, но развивающимся довольно вяло! — реализация Python, позволяющая в коде на Python вовсю и с удовольствием пользоваться почти всеми примитивами Java и ощутить преимущества JVM по максимуму. Выглядит это приблизительно так (пример из стандартной документации):
from javax.tools import (ForwardingJavaFileManager, ToolProvider, DiagnosticCollector,)names = ["HelloWorld.java"]compiler = ToolProvider.getSystemJavaCompiler()diagnostics = DiagnosticCollector()manager = compiler.getStandardFileManager(diagnostics, none, none)units = manager.getJavaFileObjectsFromStrings(names)comp_task = compiler.getTask(none, manager, diagnostics, none, none, units)success = comp_task.call()manager.close()
Кроме того, в Jython есть свое решение старой доброй проблемы с GIL — здесь его тупо нет. То есть присутствует весь тот же набор примитивов для работы с потоками, только он использует не напрямую потоки операционной системы, а их реализацию в Java-машине. Примеры работы с такого рода многопоточностью (одновременно с использованием примитивов как из Python, так и из Java) можно посмотреть здесь.
Нужно больше Java!
Любителям поинтегрироваться с Java имеет смысл обратить взор на проекты JPype, Jepp и JPE. В отличие от Jython, они не являются в полном смысле слова альтернативными реализациями, а лишь предоставляют слой доступа из CPython для манипулирования Java-классами и взаимодействия с JVM, ну и наоборот.
IronPython
IronPython, в свою очередь, написан на C# и позволяет запускать Python внутри .NET, одновременно открывая доступ к куче встроенных сущностей платформы. Код на нем может выглядеть, например, так:
from System.Net import WebRequestfrom System.Text import Encodingfrom System.IO import StreamReaderPARAMETERS="lang=en&field1=1"request = WebRequest.Create('http://www.example.com')request.ContentType = "application/x-www-form-urlencoded"request.Method = "POST"bytes = Encoding.ASCII.GetBytes(PARAMETERS)request.ContentLength = bytes.LengthreqStream = request.GetRequestStream()reqStream.Write(bytes, 0, bytes.Length)reqStream.Close()response = request.GetResponse()result = StreamReader(response.GetResponseStream()).ReadToEnd()print result
Выглядит виндово, но на практике используется довольно широко. Существует отличный онлайн-cookbook, в котором можно почерпнуть еще больше толковых примеров. Также не стоит забывать, что в придачу к .NET идет еще и расширенная поддержка Visual Studio, что может быть довольно серьезным фактором в глазах любителей этой навороченной IDE от ребят из Редмонда.
Заключение
Как видишь, террариум оказался довольно велик и есть много реализаций одного из самых популярных современных языков общего назначения, что еще сильнее расширяет круг решаемых им задач. Пусть даже поддержка новых стандартов появляется в альтернативных интерпретаторах с запаздыванием — но зато все синтаксические и архитектурные проблемы в таком случае уже обкатаны на CPython. Кроме того, разве это не круто — не только знать два разных языка, но еще и уметь оперировать одним в окружении другого? Удачной охоты на змей!