Pypy как пользоваться windows

Установка и использование PyPy3, совместимость с Python3.

Цитата:

«Если необходимо, чтобы Ваш код работал быстрее,
то вероятно, следует просто использовать PyPy»

Гвидо ван Россум (создатель Python).

Общие сведения о PyPy.

Компилятор PyPy3 — это полноценная замена CPython. Он построен с использованием языка RPython, который был разработан совместно с ним. Основная причина использовать его вместо CPython — скорость: обычно он работает быстрее.

Цель PyPy — получить скорость, но при этом поддерживать (в идеале) любую программу Python.

На данный момент (март 2021 года) PyPy, выпуска 7.3.3, реализует максимальную версию Python 3.7.9. Он поддерживает почти весь CPython, проходя набор тестов на PyPy3.6 и большую часть набора тестов PyPy3.7 (с небольшими изменениями). Он поддерживает большинство обычно используемых модулей стандартной библиотеки Python. Известные различия с CPython смотрите в разделе совместимости.

Поддерживаются и обслуживаются следующие архитектуры ЦП:

  • x86 (IA-32) и x86_64,
  • платформа ARM (ARMv6 or ARMv7, with VFPv3),
  • AArch64,
  • PowerPC 64bit как с прямым, так и с обратным порядком байтов,
  • System Z (s390x),

Скорость исполнения кода компилятором PyPy.

Основной исполняемый файл pypy поставляется с компилятором Just-in-Time. Он действительно быстро запускает большинство тестов, включая очень большие и сложные приложения Python, а не только 10-строчные.

Два случая, когда PyPy не сможет ускорить код:

  • Кратковременные процессы: если PyPy запускается со скриптами работающими меньше 2-х секунд, JIT-компилятору не хватит времени для разгона.
  • JIT-компилятор не поможет, если все время исполнения программы тратится в подключаемых C-библиотеках, а не на выполнение кода, написанного на Python.

Таким образом, лучше всего PyPy работает при выполнении длительно выполняющихся программ, когда значительная часть времени тратится на выполнение кода Python. Это случай, охватываемый большинством проводимых тестов.

Установка PyPy3 на ОС Windows:

Установка PyPy3 ни чем не отличается от установки классического Python3. Загрузить исходники PyPy3 для ОС Windows можно с официальной страницы. Дистрибутив PyPy3 Windows 32 bit совместим с любыми 32- или 64-битными ОС Windows.

Так же, может понадобиться установщик библиотеки времени выполнения VC. Загрузить файл vcredist.x86.exe можно с официальной страницы https://www.pypy.org

PyPy3 для ОС Windows готов к запуску сразу после установки из .exe или .msi файла.

Установка PyPy3 на Linux (ОС Ubuntu/Debian):

Команда разработчиков PyPy предоставляет предварительно скомпилированные двоичные файлы для многих платформ и ОС. Загрузить исходники PyPy3 можно с официальной страницы.

PyPy готов к запуску сразу после распаковки его из tarball или zip-архива, без необходимости устанавливать его в каком-либо конкретном месте:

Если необходимо сделать PyPy доступным для всей системы, то можно создать символическую ссылку на исполняемый файл ln -s /opt/pypy3.7-v7.3.3-linux64/bin/pypy /usr/local/bin/pypy . Важно разместить символическую ссылку, а не перемещать туда двоичный файл, иначе PyPy не сможет найти свою библиотеку.

Установка дополнительных модулей для PyPy3.

Если необходимо установить сторонние библиотеки, наиболее удобный способ — установить менеджер пакетов pip с помощью инструмента ensurepip . Если вы не хотите устанавливать virtualenv , то тогда можно напрямую использовать pip внутри виртуальной среды исполнения:

Если нужно иметь возможность использовать pip непосредственно из командной строки, то необходимо использовать аргумент —default-pip при вызове surepip . Сторонние библиотеки будут установлены в pypy-xxx/site-packages . Как и в случае с CPython, скрипты в linux и macOS будут в pypy-xxx/bin , а в Windows они будут в pypy-xxx/Scripts .

Установка PyPy3 в виртуальную среду исполнения virtualenv .

Наиболее удобно запускать PyPy3 внутри виртуальной среды исполнения virtualenv . Для этого необходимо установить версию virtualenv -1.6.1 или выше. Затем в установленную среду выполнения, можно установить PyPy как из предварительно скомпилированного архива, так и из разархивированной директории, после проверки PyPy3 на работоспособность.

Внимание! Не используйте для установки PyPy3 встроенный модуль venv (т.к. venv не копирует компилятор PyPy, а ставит на него только ссылки), создавайте виртуальную среду исполнения только при помощи virtualenv .

Обратите внимание, что pypy-env/bin/python теперь является символической ссылкой на pypy-env/bin/pypy , следовательно можно запускать pypy, просто набрав python .

Для PyPy3, установленного в virtualenv все равно необходимо обновить pip и wheel до последних версий через:

Совместимость с классическим Python3.

PyPy3 реализует версию языка Python 3.7.9. Он был выпущен, но Python — большой язык, и вполне возможно, что кое-что отсутствует.

PyPy3 поддерживает API CPython C, однако есть несовместимые конструкции. Команда разработчиков настоятельно рекомендует использовать CFFI, который уже встроен в дистрибутив PyPy3. Для работы небольшого количества библиотек потребуется немного усилий, но есть известные истории успеха.

Расширения языка C должны быть перекомпилированы для PyPy, чтобы они работали. В зависимости от вашей системы сборки это может работать из коробки или будет немного сложнее.

В основном PyPy3 поддерживает стандартные библиотечные модули. Обратите внимание, что многие модули python3 реализованы на чистом Python, следовательно они точно будут работать. Пожалуйста, просто проверьте, сможет ли PyPy3 на вашей системе импортировать следующие модули:

__builtin__ , __pypy__ , _ast , _cffi_backend , _codecs , _collections , _continuation , _csv , _file , _hashlib , _io , _locale , _lsprof , _md5 , _minimal_curses , _multibytecodec , _multiprocessing , _pickle_support , _pypyjson , _random , _rawffi , _sha , _socket , _sre , _ssl , _struct , _testing , _warnings , _weakref , array , binascii , bz2 , cStringIO , cmath , cppyy , cpyext , crypt , errno , exceptions , fcntl , gc , imp , itertools , marshal , math , mmap , operator , parser , posix , pwd , pyexpat , pypyjit , select , signal , symbol , sys , termios , thread , time , token , unicodedata , zipimport , zlib .

Если PyPy3 импортирует вышеуказанные модули без ошибок, то он полностью совместим с вашим Python3 и должен работать без каких либо ошибок.

Поддерживается и написано на чистом Python: cPickle , ctypes , datetime , dbm , _functools , grp , readline , resource , sqlite3 , syslog .

Все сторонние модули, которые написаны на чистым python в CPython, конечно будут работать после успешной установки.

Читайте также:  Wine для linux или windows

Различия, связанные со стратегиями сбора мусора.

Сборщики мусора, используемые или реализованные PyPy3, не основаны на подсчете ссылок, поэтому объекты не освобождаются мгновенно, когда они больше недоступны. Наиболее очевидный эффект от этого заключается в том, что файлы (и сокеты и т. д.) не закрываются сразу после выхода за пределы области видимости. Это отличие от классического Python3, не будет изменяться командой разработчиков.

Следующий код заполнит файл не сразу, а только через определенный промежуток времени, когда GC выполнит сборку мусора и очистит вывод:

Правильное использование заключается в следующем:

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

PyPy3 можно запустить с параметром командной строки -X track-resources (например, pypy -X track-resources myprogram.py ). Это вызывает ResourceWarning , когда GC закрывает незакрытый файл или сокет. Также дается трассировка для места, где был открыт файл или сокет, что помогает найти места, где отсутствует метод .close() .

Точно так же помните, что необходимо закрывать неизрасходованный генератор, чтобы ожидающие завершения операторы finally или with выполнялись немедленно:

В более общем смысле, методы __del__() не выполняются так же предсказуемо, как на CPython: в PyPy3 они запускаются через некоторое время (или не запускаются вообще, если программа тем временем завершает работу).

Обратите внимание, что PyPy3 возвращает неиспользуемую память операционной системе, если есть системный вызов madvise() (по крайней мере в Linux, OS X, BSD) или в Windows. Важно понимать, что можно не увидеть этого в выводе команды терминала top .

Сказанное выше верно как для CPython, так и для PyPy. Могут возникнуть различия в том, будет ли встроенная функция или метод вызывать переопределенный метод другого объекта, кроме self . В PyPy они часто вызываются в тех случаях, когда CPython этого не делает.

Игнорируемые исключения.

Во многих случаях CPython может молча проглатывать исключения. Точный список случаев, когда это происходит, довольно длинный, хотя большинство случаев очень редки. Наиболее известные места — это настраиваемые расширенные методы сравнения (например, __eq__ ); поиск по словарю; вызовы некоторых встроенных функций, таких как isinstance() .

Если это поведение явно не предусмотрено конструкцией и не задокументировано как таковое (например, для hasattr() ), в большинстве случаев PyPy будет поднимать исключения.

Идентичность встроенных типов ( is и id ).

Идентичность объектов примитивных типов работает по равенству значений, а не по идентичности id . Это означает, что x + 1 is x + 1 всегда верно для произвольных целых чисел x . Правило распространяется на следующие встроенные типы:

  • int ;
  • float ;
  • long ;
  • complex ;
  • str (только пустые или односимвольные строки)`;
  • unicode (только пустые или односимвольные строки)`;
  • tuple (только пустые кортежи)`;
  • frozenset (только пустой frozenset )`.

Это изменение также требует некоторых изменений в id . id выполняет следующее условие: x is y id(x) == id(y) . Поэтому id вышеперечисленных типов будет возвращать значение, которое вычисляется из аргумента, и, таким образом, может быть больше, чем sys.maxint (т. е. может быть произвольно длинным).

Обратите внимание, что строки длиной 2 или более могут быть равны, не будучи идентичными. Аналогично, x is (2,) не обязательно истинно, даже если x содержит кортеж и x == (2,) . Правила уникальности применимы только к частным случаям, описанным выше. Правила str , unicode , tuple и frozenset были добавлены в PyPy выпуск 5.4; до этого тест типа if x is «?» или if x is () мог потерпеть неудачу, даже если x был равен «?» или () . Новое поведение, добавленное в PyPy выпуск 5.4, ближе к CPython, который кэширует именно пустой tuple / frozenset и (как правило, но не всегда) str и unicode длинной float существует только один объект на “битовый шаблон” float . Таким образом, float(‘nan’) is float(‘nan’) истинно на PyPy3, но не на CPython, потому что они являются двумя объектами; но 0.0 is -0.0 всегда False , так как битовые шаблоны различны. Как обычно, float(‘nan’) == float(‘nan’) всегда ложно. При использовании в контейнерах (например, в виде элементов list или в set ) точное правило равенства используется так: “ if x is y or x == y ” (как на CPython, так и на PyPy); как следствие, поскольку все nan идентичны в PyPy3, вы не можете иметь несколько из них в множестве set , в отличие от CPython.

Другим следствием является то, что cmp(float(‘nan’), float(‘nan’)) == 0 , потому что cmp() сначала проверяет is , идентичны ли аргументы (нет хорошего значения для возврата из этого cmp() , так как функция cmp() делает вид, что существует полный порядок для чисел с плавающей запятой, но это неверно для NaN ).

Различия в производительности.

CPython имеет оптимизацию, которая может сделать повторную конкатенацию строк неквадратичной. Например, такой код выполняется за время O(n) :

В PyPy3 этот код всегда будет иметь квадратичную сложность. Также обратите внимание, что оптимизация CPython хрупкая и в любом случае может сломаться из-за небольших изменений в коде. Так что все равно необходимо заменить код выше на:

В принципе это основные отличия с которыми сталкивается 80% разработчиков.

Установка PyPy в Windows

Чтобы установить pypy в Windows необходимо проделать следующие шаги:

  1. Скачать дистрибутив для вашей OS
  2. Распаковать в директорию, например C:\
  3. Добавить путь `C:\pypy` в переменную PATH (либо через Мой компьютер → Свойства → Дополнительные параметры системы → Дополнительно → Переменные среды)
  4. Открыть командную строку
  5. Перейти в директорию вашего .py файла
  6. Ввести: pypy filename.py

Как видите, ничего сложного. Один в один как при установке классического cpython

Далее, если вы хотите использовать виртуальное окружение в разработке своего проекта, необходимо его создать так:

virtualenv -p pypy3 myprojectdir

virtualenv -p pypy myprojectdir

Скрипт для активации activate.bat виртуального окружения находится в папке /bin вашего проекта.

В папку site-packages находятся все устанавливаемые вами модули. По умолчанию это pip, wheel (для управления пакетами) и setuptools (управление версиями пакетов). Дальнейшая работа с ничем не отличается от работе через cpython

Если вы работаете с джанго, `django-admin startproject hello` и вперед.

Но должен вас предупредить. что в процессе работы вы можете столкнуться с некоторыми проблемами, например, при использовании модуля pillow

Читайте также:  Не завершается сеанс windows

PIP в PYPY:

После установки pypy можно столкнуться с проблемой отсутствия pip. В этом случае выполните команду: pypy — m ensurepip

Руководство: пишем интерпретатор с JIT на PyPy

Все исходные коды и примеры из этой статьи доступны здесь.

Когда я первый раз смотрел проект PyPy, мне потребовалось некоторое время, чтобы выяснить, что он из себя представляет. Он состоит из двух вещей:

— набор инструментов для написания интерпретаторов языков программирования;
— реализация Питона с применением этого набора инструментов.

Вероятно, большинство людей думает, что PyPy это только вторая часть, но это руководство не об интерпретаторе Питона. Оно о том, как написать интерпретатор своего языка.

Я взялся за это руководство для того, чтобы лучше понять как работает PyPy и что он из себя представляет. Предполагается, что вы очень мало знаете о PyPy, поэтому я начну с самого начала.

Чем является PyPy

Предположим, что мы хотим написать интерпретатор. Это включает в себя написание парсера исходного кода, цикла исполнения байт-кода и большого количества кода стандартной библиотеки.

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

И даже тогда вы должны заботиться об управлении памятью в интерпретаторе и должны будете реализовывать типы данных, такие как целые числа произвольной размерности, таблицы хешей и прочее. Этого достаточно, чтобы многие передумали реализовывать свой интерпретатор.

Разве не было бы замечательно, если бы вы могли реализовать свой язык на высокоуровневом языке, например, Питоне? Вы бы получили в свое распоряжение все преимущества высокоуровневого языка, такие как автоматическое управление памятью и богатый набор типов данных. Но интерпретация языка на другом интерпретируемом языке должна быть очень медленной, верно?

Как можно догадаться, PyPy решает эту проблему. PyPy — это сложный инструментарий для анализа и перевода вашего кода интерпретатора в Си (или JVM или CLI). Этот процесс называется «трансляцией». Он знает как перевести не весь синтаксис Питона, но довольно большую его часть. Все что вы должны сделать — написать свой интерпретатор на языке RPython, подмножестве языка Питон, после этого вы получите очень эффективный интерпретатор.

Потому что написание эффективных интерпретаторов не должно быть проблемой.

Язык, который я выбрал для реализации, ужасно простой. Рантайм языка состоит из ленты целых чисел, инициализированных нулем, и одного указателя на текущую ячейку в этой ленте. Язык имеет всего 8 команд:

— переместить указатель следующую ячейку.

+ — увеличить на один число в текущей ячейке.

— уменьшить на один число в текущей ячейке.

[ — если число в текущей ячейки — 0, то пропустить все инструкции до соответствующей инструкции ].

] — вернуться назад к соответствующей инструкции [.

. — вывести в стандартный поток вывода один байт из текущей ячейки.

, — прочитать один байт из стандартного потока ввода и положить в текущую ячейку.

Любые нераспознанные символы должны игнорироваться.

Некоторые могли узнать этот язык, это brainfuck.

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

Первые шаги

Давайте начнем с того, что напишем интерпретатор на обычном Питоне. Вот набросок основного цикла выполнения.

Как видите, счетчик команд (pc) хранит указатель на текущую инструкцию. Первое выражение в цикле извлекает инструкцию, затем несколько условных операторов определяют, как её выполнять.

Реализация операторов «[» и «]» опущена, они должны менять счетчик команд на положение совпадающей скобки.

А теперь реализация класса Tape, который хранит ленту целых чисел и указатель на текущее число.

Как видите, лента при необходимости увеличивается. На самом деле стоило бы добавить проверку на ошибки, когда указатель становится отрицательным. Но пока это неважно.

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

Эта функция возвращает строку только из команд языка и словарь парных скобок.

Осталось соединить это, и у нас получится рабочий интерпретатор brainfuck.

Полный код интерпретатора, включая реализацию квадратных скобок, можно посмотреть в первом примере. example1.py

Теперь вы можете попробовать запустить интерпретатор на Питоне, чтобы убедиться, что он работает.

$ python example1.py 99bottles.b

Трансляция PyPy

Но наша цель была не только в написании интерпретатора brainfuck. Что же нужно сделать, чтобы PyPy создал из этого кода супербыстрый исполняемый файл?

В исходниках PyPy, в папке pypy/translator/goal лежат простые примеры, которые придутся кстати. Для начала заглянем в targetnopstandalone.py — простой hello world для PyPy.

Важно то, что модуль содержит функцию target, которая возвращает точку входа. Трансляция начинается с этой точки.

Функция entry_point будет принимать аргументы командной строки, когда вы будете запускать получившийся исполняемый файл.

RPython

Давайте поговорим о RPython. PyPy не может транслировать обычный код на Питоне, потому что Питон слегка слишком динамичный. Есть некоторые ограничения, которые накладываются на стандартную библиотеку и синтаксис Питона, чтобы PyPy мог транслировать его. Я не буду перечислять их все, лучше посмотреть их в документации.

В вышеприведенном примере пришлось изменить несколько вещей. Теперь приходится использовать низкоуровневые дескрипторы с функциями os.open и os.read вместо использования файловых объектов. Реализация «.» и «,» тоже немного изменена. Это все изменения, необходимые для того, чтобы PyPy стал переваривать код.

Это было не слишком сложно, правда? Я продолжаю использовать словари, расширяемые списки и даже классы с объектами. И если файловые дескрипторы для вас слишком низкоуровневые, в модуле rlib.streamio, поставляемом вместе со стандартной библиотекой RPython, есть несколько полезных абстракций.

Полный код теперь выглядит так: еxample2.py

Трансляция

Если вы этого еще не сделали, слейте последнюю версию PyPy с репозитория на bitbucket.org.

Скрипт, который нужно запустить — pypy/translator/goal/translate.py. В качестве параметра он принимает модуль, который нужно оттранслировать.

$ python ./pypy/pypy/translator/goal/translate.py example2.py

Для более быстрой трансляции можно использовать PyPy вместо Питона.

Результатом выполнения станет исполняемый файл — интерпретатор brainfuck. В репозитории лежит генератор фракталов на brainfuck, выполнение которого на моей машине занимает около 45 секунд. Попробуйте сами.

Читайте также:  Эмулятор геймпада ps2 для pc windows 10

И сравните скорость с тем, что выдает этот же интерпретатор, запущенный на Питоне.

$ python example2.py mandel.b

Итак, мы написали интерпретатор на RPython и оттранслировали его с помощью инструментария PyPy.

Добавляем JIT

Трансляция RPython в Си — это конечно круто, но одна из главных фич PyPy — это возможность генерировать компилятор времени выполнения (JIT). Используя всего несколько подсказок о том, как устроен ваш интерпретатор, PyPy сгенерирует JIT-компилятор, который будет переводить интерпретируемый код brainfuck в машинный.

Чтобы это произошло, PyPy должен знать, где начинается цикл выполнения программы. Это позволяет отследить, какие инструкции выполняются на brainfuck.

Мы также должны указать особенности цикла выполнения. Так как в нашем языке нет стека, нам нужно только указать какие переменные относятся к коду программы, а какие к её данным. Это называется зелеными и красными переменными соотвественно.

Вернемся ко второму примеру.

В нашем основном цикле выполнения используются четыре переменных: pc, program, bracket_map и tape. Конечно, pc, program и bracket_map — зеленые переменные, они определяют выполнение интерпретируемой программы. Переменная tape — красная, она изменяется при выполнении интерпретируемой программы.

Давайте сообщим PyPy эти данные. Начнем с импорта класса JitDriver и создания его экземпляра.

И добавим такую строку в самое начало цикла исполнения:

Также нам нужно определить JitPolicy.

Полный текст примера: example3.py

Теперь еще раз оттранслируем код, но уже с флагом —opt=jit:

$ python ./pypy/pypy/translator/goal/translate.py —opt=jit example3.py

Трансляция будет идти значительно дольше, почти 8 минут на моей машине, и получившийся исполняемый файл будет намного больше. После окончания трансляции запустим программу генерации фракталов снова. Разница огромна — примерно 12 секунд против 45 в предыдущем варианте!

Как видите, JIT-компилятор действительно использовал машинный код вместо интерпретации. Несколько первых линий картинки выводятся достаточно быстро, потом программа ускоряется и остальное выводится еще быстрее.

Немного о трассирующих JIT-компиляторах

Стоит поговорить о том, как вообще работают трассирующие JIT-компиляторы. Ваш интерпретирующий код запускается в обычном режиме. Когда JIT встречает часто выполняющийся цикл в интерпретируемом языке (brainfuck), цикл помечается для трассировки. Когда в следующий раз достигается этот же цикл, включается логирование каждой выполненной инструкции.

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

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

Больше информации можно найти в Википедии.

Отладка и логи трассировки

Можем ли мы увидеть, что делает JIT?

Давайте добавим функцию get_printable_location, которая будет использоваться для вывода отладочной информации.

Эта функция принимает зеленые переменные и возвращает сроку. Мы выводим код brainfuck, в котором текущая выполняемая инструкция окружена подчеркиваниями.

Оттранслируйте код примера example4.py.

Теперь запустите тестовую программу (test.b просто выводит букву «A» примерно 15 раз) с выводом логов трассировки.

$ PYPYLOG=jit-log-opt:logfile ./example4-c test.b

Файл logfile содержит логи всех произведенных трассировок и позволяет взглянуть на то, какие инструкции были скомпилированы в машинный код. Файл полезен тем, что позволяет увидеть ненужные инструкции или пути для оптимизации.

Каждая трассировка начинается с линии вроде этой:
[3c091099e7a4a7]

И заканчивается такой линией:
[3c091099eae17d jit-log-opt-loop>

Сразу после заголовка трассировки идет комментарий с порядковым номером и количество операций. В моем случае первая трассировка выглядит так.

Я немного обрезал слишком длинные строки debug_merge_point.

Этот участок кода принимает четыре параметра: два указателя на объекты (p0 и p1) и два числа (i2 и i3).

Первый оператор «>» начинается на 4-й строке. Он выполняется без инструкций и выглядит окончательно оптимизированным. Этот цикл всегда работает с одной частью ленты, и указатель на текущую ячейку остается постоянным.

Строки с пятой по восьмую — оператор «+». Сначала извлекается элемент массива с индексом i2 из указателя p1 (строка 6), прибавляется единица и сохраняется в i6 (строка 7). Результат помещается обратно в массив (строка 8).

Строка 9 соответствует инструкции « , которой передается один параметр, p0.

Если же проверка пройдена, строки с 17 по 23 достают из словаря bracket_map адрес инструкции, к которой нужно перейти. Я не уверен, что именно делают эти строки, но видно, что в них содержится два внешних вызова и 3 проверки. Это слишком расточительно, учитывая, что bracket_map не меняется и результатом будет один и тот же адрес, на который нужно перейти. Но PyPy об этом не знает, а мы знаем, поэтому можем оптимизировать это место.

Строка 24 увеличивает полученный из bracket_map указатель. Строки 25 и 26 проверяют, что он не превысил длину программы.

Кроме этого в строке 27 проводится дополнительная проверка, что указатель строго равен 86. Это нужно для того, чтобы убедится, что прыжок должен быть сделан в начало цикла.

В конце цикл замыкается на строке 28, и в строке 29 происходит прыжок в начало цикла с параметрами p0, p1, i2, i3.

Оптимизация

Как было замечено, на каждой итерации цикла происходит поиск в словаре для нахождения парной скобки. Это ужасно неэффективно, потому что цель перехода не меняется на разных итерациях.

Нам нужно дать еще одну подсказку транслятору, чтобы сказать, что запрос к словарю всегда вернет одинаковые элементы для одинаковых индексов словаря.

Чтобы это сделать, мы вынесем обращение к словарю в отдельную функцию и обернем её pypy.rlib.jit.purefunction.

Эта версия может быть найдена в example5.py.

Оттранслируйте этот пример. Мандельброт теперь выполняется за 6 секунд вместо 12!

Давайте взглянем на новый лог трассировки.

Намного лучше! Теперь каждый цикл — это только одно сложение, вычитание, две загрузки из массива, два помещения в массив и проверка при выходе. Код не требует никаких изменений счетчика команд.

Эту оптимизацию мне подсказал Армин Риго в рассылке pypy-dev. У Карла Фридриха есть несколько статей об оптимизации интерпретаторов, которые тоже оказались полезными.

Заключение

Я надеюсь, эта статья убедила вас в том, что PyPy это не только быстрый интерпретатор Питона.

Для тех из вас, кто хочет больше узнать о JIT-компиляторе PyPy, я рекомендую прочитать статью Tracing the Meta-Level: PyPy’s Tracing JIT Compiler.

Оцените статью