[cython] разбор простой задачи
1) для такой задачи лучше подойдет numpy - с лету сказать сложно, но подходящий функционал в нем есть. в scipy возможно даже готовая функция найдется. для примера можно и самому проимплементировать - ок, идем дальше
2) зачем тут dict? это неупорядоченная структура для хранение разнотипных данных. тут больше уместен простой list - как по функциональности, так и по самому решению. с list этот же пример будет читаться лучше и выполняться быстрее.
3) ну ладно уж, взяли dict, идем дальше. а что дальше - пробегаемся по нему полностью (еще раз - это дорогая в смысле производительности структура данных) чтобы суммировать, а затем второй раз тоже до какого-то места и тоже суммируя! чтобы стало быстрее надо очевидно изменить алгоритм таким образом чтобы пробегаться было достаточно только один раз. посчитать как это влияет можно на примерах типа {1:1, 2:1, 3:1, 4:1, 5:95} {1:1, 2:1, 3:95, 4:1, 5:1} , {1:95, 2:1, 3:1, 4:1, 5:1}. Для какого то из этих примеров (насколько помню, dict не гарантирует порядка данных, поэтому нельзя сказать наперед для какого именно) второй цикл будет отрабатываться быстрей всего, для какого-то дольше всего. Из этой разницы можно оценить какой вклад вносит доступ к элементу dict в общее время выполнения.
так что общее направление оптимизации (если без применения сторонней библиотеки типа numpy/scipy) - это переписать с использованием list с одновременным уменьшением числа пробегов по нему. как именно - ну надо думать.
сам пример - скорей на тему как не стоит делать. этот якобы cython код на самом деле дергает очень много cpython, поэтому и переписывание дает мало. для полноты картины - сравнивать надо бы на больших объемах данных, а не только на большом количестве итераций.
и да - если я выше где-то облажался, прошу считать меня ни на что не претендующим ньюбом
Касательно улучшения алгоритмов и типов — это следующий шаг (сравнить через list/array, попробовать vector).
Итерации тут больше для:
первичное: растяжения времени, чтобы наглядно видеть прирост скорости выполнения,
вторичное: провести большую выборку, чтобы наглядно убедиться в вероятности выпадения при единичном вызове.
scipy/numpy тоже в планах (тем более cython с numpy вообще на "ты" на первом этапе интересны средства "из коробки".
Сейчас меня волнует, правильно ли я "сатанизировал" православный Python, т.к. раньше подобного (конкретно с ним) делал.
Строчек прибавилось 0, время сократилось в 3 раза
>>>> import _randoms
10.0 10.01
20.0 20.0
15.0 15.0
25.0 24.99
30.0 30.0
Time Taken: 0:00:03.480000
А статически типизированный код не завелся. Pypy скушал гигабайт памяти и прекратил работу.
Позже попробую более обдуманно собрать.
А если интерфейс простой, и обмен идёт только базовами типами, строками и сишными массивами, то вообще ничего не надо, есть ctypes.
Ну и как тут говорят если нужна не суперскорость а просто скорость, то можно pypy заюзать бесплатно.
Или если задача векторизуется то можно всё в numpy делать. И это не только к численным методам относится.
У цитона нет ниши.
Ы?
Гуглинг по фразе "static typed python" вывел на цитон.
Почитал доки, сел вчера написать хрень из первого поста. Безрассудная фигачилка типов дала прирост в два раза.
Как я к этому отношусь, пока не решил. Между 12 и 5 сек разница существенная, но между и не особо велика.
Нужно разобрать больше примеров, имеющих отношение к сфере деятельности, чтобы прийти к заключению. Но сперва надо понять, всё ли правильно объявлено тут или упустил чего.
И да, возможно, я нагуглил не тот инструмент для анализа.
Между 12 и 5 сек разница существенная, но между ... и ... не особо велика.вообще-то одинаковая - в 2.4 раза
Не ясно выразился. В плане человеческого ожидания.
# _randoms.py
from random import random
from datetime import datetime
def takeone(l):
rnd = random * sum(l)
boundary = 0
for i in range(len(l:
boundary += l[i]
if rnd < boundary:
return i
values = ['ru','us','en','de','fr']
chances = [ 10, 20, 15, 25, 30 ]
results = [0 for i in chances]
r = 10**7
start = datetime.now
for i in range(r):
results[takeone(chances)] += 1
end = datetime.now
for i in range(len(chances:
print values[i], round(100.0*chances[i]/sum(chances 2 round(100.0*results[i]/sum(results 2)
print 'Time Taken: %s'%(end-start)
# randoms.pyx
from random import random
from datetime import datetime
def takeone(list l):
cdef:
int i
float rnd = random * sum(l)
int boundary = 0
for i in range(len(l:
boundary += l[i]
if rnd < boundary:
return i
cdef:
int i
list values = ['ru','us','en','de','fr']
list chances = [ 10, 20, 15, 25, 30 ]
list results = [0 for i in chances]
int r = 10**7
start = datetime.now
for i in range(r):
results[takeone(chances)] += 1
end = datetime.now
for i in range(len(chances:
print values[i], round(100.0*chances[i]/sum(chances 2 round(100.0*results[i]/sum(results 2)
print 'Time Taken: %s'%(end-start)
В православном время выполнения не поменялось, в собранном дало прирост 4х.
>>> import _randoms
ru 10.0 10.01
us 20.0 19.99
en 15.0 15.0
de 25.0 25.01
fr 30.0 29.99
Time Taken: 0:00:12.190000
>>> import randoms
ru 10.0 9.99
us 20.0 20.01
en 15.0 15.01
de 25.0 24.97
fr 30.0 30.01
Time Taken: 0:00:03.080000
pypy всех сделал.
>>>> import _randoms
ru 10.0 10.0
us 20.0 19.97
en 15.0 15.01
de 25.0 25.0
fr 30.0 30.02
Time Taken: 0:00:01.758000
Прошелся по Гуглу чуть ниже первых строк. Оказывается, сам Г. ван Россум думает от статической типизации.
Касательно цитона я так понял, основная (полезная) идея в доступе из Py2, Py3 и C. Пригодится, если появится необходимость перейти с версии 2 на 3.
А пока, да, достаточно pypy. Спасибо отозвавшимся.
Сишные куски мы пробовали делать пару лет назад, итог неутешителен - прирост производительности в пару раз, зато оптимизировать получающуюся кашу очень сложно.
cython в принципе не придумали зачем нужен.
Но я вот в упор не понимаю зачем нужен стрёмный диалект си с псевдопитоновым синтаксисом, когда есть нормальный си, а то и си++ и вполне себе mature SWIG чтобы к питону это присобачить.Использовать cython для биндинга C кода проще и удобнее.
Оставить комментарий
khachin
Решил тут поиграться со статической типизацией. Задачу взял .Вышло нечто такое:
Запускаем в среде:
Строк прибавилось на 7, время сократилось более, чем в 2 раза.
У кого есть опыт в "сатанизации"? Что еще можно улучшить конкретно в данном примере?
Оффтоп: На Galaxy Note 3 в qpython выполнение исходного скрипта заняло 0:01:09.640000