Pull to refresh

Comments 41

Стоило бы еще упоминуть про cython: для арифметики он нормально заменяет ctypes и при этом более дружелюбный для тех кто не знает или не любит C. Кроме того он еще и более менее нормальные классы понятные для питона позволяет писать.

немного найденого кода из pygame PaintBrush
cimport cython
import operator
import math

cdef extern from "math.h":
    double sqrt(double x)
    double atan2(double y, double x)
    double cos(double x)
    double sin(double x)
    double floor(double x)

cdef class v2d(object):
    cdef public double x, y
    def __init__(self, x, y=None):
        if x is not None and y is not None:
            self.x = float(x)
            self.y = float(y)
        elif type(x) == v2d:
            self.x = x.x
            self.y = x.y
        elif type(x) == list or type(x) == tuple:
            self.x = x[0]
            self.y = x[1]
        else:
            raise TypeError()
    
    # Position
    def get_pos(self):
        return (self.x, self.y)
    def set_pos(self,pos):
        self.x = float(pos.x)
        self.y = float(pos.y)
    pos = property(get_pos, set_pos)

    def get_int_pos(self):
        cdef int x, y
        x = int(floor(self.x))
        y = int(floor(self.y))
        return(x,y)
    ipos = property(get_int_pos, set_pos)

# ...


А в некоторых случаях можно вообще писать чистый питон код и он его «ускорит».
>>А в некоторых случаях можно вообще писать чистый питон код и он его «ускорит».
Пример пожалуйста, а то юзаю как плюсы так и питон. Но навскидку не могу привести пример Ваших «ускорит», а очень интересно!
Ну удачный случай я подобрать не смог, а все свои старые эксперименты я потерял.

Вот файлы для проверки:
gist.github.com/4030438

imp@home ~/zz/cython $ python pyt.py 
12.2809770107
imp@home ~/zzz/cython $ python cyt.py
10.8371829987


Отрыв хоть и небольшой, но сохраняется постоянно. У них там есть какая-то магия с numpy — вроде всё что использует его может ускорится.

Ну а в целом всё-равно нужно прибегать к дополнительной магии: если добавить еще один файл который подскажет cython`у где какие типы переменных, то отрыв сразу увеличится, а модуль при этом всё-равно можно будет импортировать как скомпилированный, так и не скомпилированный,

Например в файл v2d.pxd добавим такое:
cimport cython

cdef class v2d(object):
    cdef public float __x, __y


и запустим заново:
imp@home ~/zzz/cython $ python cyt.py
8.40173888206
...
8.0


И так можно дальше улучшать, например как в предыдущем комментарии взять функции из math.h и использовать их. Еще можно взять и переопределить функции:
cimport cython

cdef class v2d(object):
    cdef public float __x, __y

    cpdef float get_length(self)

В данном случае это прироста в скорости не дало, может быть даже замедлило

Еще можно обойтись и без создания pxd и писать прямо в .py файле сохраняя совместимость (см v2d_pure.py)
В данном случае вс' стало даже медленнее.

Реальные ускорения можно получить если поступать так:
Делать большинство методов чисто сишнымии во внтутренних рассчётах использовать из, а наружу выставлять уже функции питона

pyfile:
@cython.cfunc
@cython.returns(cython.float)
@cython.locals(x=cython.float, y=cython.float)
def __cget_length(self, x, y):
    pass

@cython.cfunc
@cython.returns(cython.float)
@cython.locals(x=cython.float, y=cython.float, z=cython.float)
def __ccomplex_calcs(self, x, y):
    z = self.__cget_length(x, y) ** 8 * self.__cget_anima()

def get_length(self, x, y):
    return self.__cget_length(x, y)

def complex_calcs(self, x,y):
    return self.__ccomplex_calcs( x, y)


Мне этот «Pure» режим что-то выглядит стрёмно — pxd файл как-то попроще.

*.pxd: +simpler, -harder to sync
@cython.decorators: -huge, +always up to date

Ну а вообще если хочется из него выжать всёи получить прирост в скорости десятки и сотни раз, то надо использовать сишные функции как в комментарии выше, писать сразу pyx файл, а не разбивать на py and pxd. В чем еще преимущество cython, так это в том, что на выходе можно получить библиотеку v2d.so и использовать её хоть прямиком из Си, так-что те кто си не любит может всё-равно выдавать для него либы
На прошлой работе использовали cython для обфускации кода.
Кое-где делали жесткую обфускацию, а коли это почти бесплатно все остальные «core.py» в каждом модуле тоже тупо переименовывали в pyx и компилили.

Факт фактом — почти любой питонячий код простая перекомпилляция в cython ускоряет)

Не знаю как дело обстоит сейчас с python 3.3 :)
Питон берет скоростью разработки. Когда нужна производительность, пайтон — далеко не лучший вариант. Иногда получаешь граблями по лбу даже там, где совсем не ожидаешь.

Вот к примеру, две одинаковые реализации флойда, тут и тут. Разницы вроде как и нет, а разница во времени выполнения ощутимая.
Это известный факт: глобальные переменные работают медленнее локальных.
Объясните новичку, почему так получается?
Для доступа к глобальной переменной используется поиск по hashtable (dict), локальные доступны просто по целочисленному индексу. Поиск по hashtable, понятное дело, гораздо дороже.
… и Оскар переходит к cython!

Вот продолжение простенького бенчмарка. Код расположен в gist — https://gist.github.com/4025567. Запускаем!

$ for power in 2 4 6 ; do for module in timing_{native,ctypes,cython,cython_range} ; do echo -n "$module: $power "; python2 -m timeit --setup "from $module import test" "test(10 ** $power)" ; done ; echo "---------" ; done
timing_native: 2 100000 loops, best of 3: 4.72 usec per loop
timing_ctypes: 2 100000 loops, best of 3: 2.27 usec per loop
timing_cython: 2 1000000 loops, best of 3: 0.21 usec per loop
timing_cython_range: 2 1000000 loops, best of 3: 0.211 usec per loop
---------
timing_native: 4 1000 loops, best of 3: 448 usec per loop
timing_ctypes: 4 10000 loops, best of 3: 30.1 usec per loop
timing_cython: 4 100000 loops, best of 3: 5.57 usec per loop
timing_cython_range: 4 100000 loops, best of 3: 6 usec per loop
---------
timing_native: 6 10 loops, best of 3: 248 msec per loop
timing_ctypes: 6 100 loops, best of 3: 2.59 msec per loop
timing_cython: 6 1000 loops, best of 3: 541 usec per loop
timing_cython_range: 6 1000 loops, best of 3: 586 usec per loop
---------


Отмечу, что можно использовать даже родные циклы через range (если переменная-итератор — int, то он будет развернут в родной сишный).

Не владею магией низкоуровнего питона, предполагаю, что у cython меньше оверхед на преобразование типов в сишные и обратно при вызове.
Ради интереса сравнивал скорость работы Delphi-проекта и python. В теле цикла сделал выражение со сложением, умножением и делением. Плюс периодичный вывод значений (раз в 10000 вроде бы, уже не помню). Результат тоже поразил: примерно в 100 раз делфи был быстрее. Далее написал библиотеку на CPython (функция-цикл вызывала переданную из python-кода функцию для вывода промежуточных результатов) и откомпилировал ее, подгрузил в Python-программу. В итоге ускорил цикл в 20 раз на windows и примерно в 15 на linux (почему такая разница получилась — не разбирался).
на живой программе с IO результаты сравнения будут совсем другими.
А если просто «считать числа», то GPU (с тем же pyopencl) порвет всех как Тузик тряпку.
Я полагаю разница из-за того, что при компиляции ctypes не были включены оптимизации, а при компиляции cython было включено -O2.
ctypes очень медленный на переходе барьера C <-> Python, если что
Ну чтобы это продемонстрировать, нужно сравнить программы откомпилированные с одинаковыми ключами, верно?
Верно для рассматриваемого примера.
Для оценки же тормозов вызова функций через ctypes нужно тестировать вызов пустой функции.
Тормоза — одна из главных причин почему в CPython stdlibrary не используется ctypes.
Вторая — ABI гораздо более изменчив чем API и поддерживать ABI совместимость заметно труднее.
Недавно эта тема опять поднималась на python-dev: code.activestate.com/lists/python-dev/118511/
Ну и для тех, кто не хочет сильно думать о типах и вызове сей из питона, я упомяну про такую штуку как SWIG, который умеет генерировать обертки.
UFO landed and left these words here
Спасибо, это мне не попадалось. Хотя, справедливости ради, скажу, что у меня после некоторых плясок с бубном (а где их нет?) SWIG тихо мирно делает то, что я от него хочу без каких либо проблем.
UFO landed and left these words here
Ага, именно с плюсами он у меня и используется.
Для плюсов больший смысл имеет boost.python
к слову
сэмпл используя boost::python (windows, boost 1.51, release)

#include <boost/python.hpp>
using namespace boost::python;

namespace Test
{ 
     unsigned long long sumrange(unsigned long long arg)
     {
          unsigned long long i, x;
          x = 0;
          for (i = 0; i < arg; i++) {
               x = x + i;
          }
          return x;
     }
}


BOOST_PYTHON_MODULE(boost_python_module)
{
     def("sumrange", Test::sumrange);
}

import timeit
from boost_python_module import sumrange as sumrange2

def sumrange1(arg):
    return sum(xrange(arg))

for power in (2, 8, 9):
    loops = 10**power
    t = timeit.Timer(lambda : sumrange1(loops))
    print("Loops: {0} Time: {1}".format(loops, t.timeit(1)))
print()
for power in (2, 8, 9):
    loops = 10**power
    t = timeit.Timer(lambda : sumrange2(loops))
    print("Loops: {0} Time: {1}".format(loops, t.timeit(1)))

Результат:
Loops: 100 Time: 8.08813168841e-06
Loops: 100000000 Time: 14.5755965742
Loops: 1000000000 Time: 143.893784101
()
Loops: 100 Time: 2.51157773619e-05
Loops: 100000000 Time: 0.173007690962
Loops: 1000000000 Time: 1.69442229668
Не понятно только, в чём смысл не использовать оптимизацию. Ещё за компанию OpenMP / GPU /…
В оригинальной статье автор сперва использует оптимизацию, но в этом случае компилятор оптимизирует алгоритм так, что время выполнения становится постоянным независимо от аргумента. Я эту часть статьи просто пропустил
Иначе говоря, оптимизации отключена, чтоб сравнивать реализацию одного и того же алгоритма.
Думаю, что автор оригинальной статьи схалтурил. Какой смысл бороться за производительность, и при этом выключать оптимизацию? Можно было сделать сумму немного сложнее, чтобы компилятор не суммировал арифметическую последовательность.
Просто автор оригинальной статьи подобрал не самый лучший синтетический пример.
А автор перевода хотел заменить его чем-нибудь поудачнне, например поиском N-го простого числа, но постеснялся, т.к. это уже не было бы переводом, а писать авторские статьи, на темы в которых плохо разбираешься не позволяет совесть.
у меня сломался CTRL-F. Я не смог найти pypy
Это к чему?
Сбываются мои худшие подозрения: все участники обсуждения, включая переводчика статьи, не имеют опыта использвания обсуждаемых инструментов в реальной работе?
Или вы таки используете PyPy в production?
не знаю на какой вопрос отвечать сначала, и не знаю тянут ли на «реальную работу» мои последние 3 года, где 90% кода работает ежедневно под pypy.
Если вы последние три года используете PyPy на работе повсеместно — извиняюсь и снимаю шляпу.
А что с оставшимися 10%? Какую версию pypy используете сейчас? С какой начали? Какие были неприятности и неожиданности? Баги?
> А что с оставшимися 10%?

1. пара модулей, где pypy странно требует больше памяти, чем CPython (ведь обычно даже меньше), но возможно дело в этом: bugs.pypy.org/issue1282

2. lxml падал в многопоточных приложениях (трудно воспроизводимо)

это из последних лет, а это из грядущего:

3. pyCUDA/Theano не собирается даже

> Какую версию pypy используете сейчас?

сборку двухнедельной давности из репозитория, где fijal допили последний принципиальный для меня issue по numpy

> С какой начали?

следил с 0.9х какой-то, а пользоваться, где-то между 1.2 и 1.3

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

Багов как таковых не было: трансляция pypy из pypy — это очень неслабый тест, который стоит в основе каждой сборки.
Если не секрет, расскажите, чем занимаетесь?
краулинг + text-mining движок

пока с одним приложением (для онлайн рекрутингового рынка), потом есть конкретные планы к другим областям применять.
Недавно попробовал на одном из алгоритмов pypy и был в детском восторге от производительности. Огорчило, что с numpy не удалось подружить – это печаль, конечно.
> Огорчило, что с numpy не удалось подружить – это печаль, конечно

что конкретно не вышло?
Вспомнить бы)
Так как numpy использовался мало – спокойно его выпилил.
Может быть я уже подзабыл и на самом деле из сторонних библиотек у меня PyYAML не завёлся. Но сейчас уже, к сожалению, не вспомнить.
Откуда эти значение по оси x 10^2, 10^8, 10^9 вы собираетесь график на логарифмической шкале рисовать?
97.8 с /10^9 = 97 наносекунд на одно число.
3.79 с /10^9 = 4 наносекунды.

Я бы не стал называть эти результаты катастрофой, потому что это интерпретируемый язык. Операции в тесте настолько просты, что они могут трансформироваться в простые asm инструкции и я не исключаю, что можно достичь даже 1 наносекунды. Я бы посмотрел на это даже с другой стороны, стандартный overhead операции 90 наносекунд, что совсем немного. Если вы не декодируете видео, то для сортировки 100 строк и вывода их на экран 1 000 000 операций в секунду вполне достаточно.
UFO landed and left these words here
UFO landed and left these words here
Если использовать boost::python, то сразу получите готовый модуль Python.
Всё что останется — сделать import.
Only those users with full accounts are able to leave comments. Log in, please.