SlideShare uma empresa Scribd logo
1 de 96
Baixar para ler offline
Ремизов Иван
Cloud Architect
Оптимизация
производительности Python
‹#›
Картинка для привлечения внимания
* CPython, без привлечения внешних зависимостей, компиляции и тп
30x
‹#›
Python
Рассматриваем язык:
Python 2.x
Конкретные реализации:
CPython (*nix default)
PyPy (JIT)
‹#›
Проблемы приложения.
Какие проблемы вообще бывают?
• неудачные архитектурные решения
• неудачно выбранные компоненты и фреймворки
• медленный I/O
• высокий расход памяти, утечки памяти
• медленный код
‹#›
Проблемы приложения.
Как решается большинство проблем?
• добавление воркеров
• кеширование
• отложенные задания, очереди
• замена компонентов
• map/reduce
• изменение архитектуры
• …
‹#›
Когда это критично и не решаемо «привычными» способами?
Обработка потоковых данных
пример: процессинг датчиков (акселерометры, гироскопы)
Десериализация
пример: JSON, pickle, ..
Авторегрессия
пример: EMA (скользящая средняя), численное интегрирование,
ряды
Стейт-машины
пример: AI, синтаксические анализаторы текста
Медленный код.
‹#›
Профилирование специальными утилитами
• ручной профайлинг (тайминг)
• статистический профайлинг (сэмплинг)
• событийный профайлинг (граф вызовов)
Логгирование и сбор статистики
• настройка конфигов apache/nginx/…
• логи приложения
Как найти критические участки кода?
‹#›
Утилиты
• profile/cprofile
• pycallgraph
• dis (иногда бывает полезно)
Profiling.
‹#›
Выбор огромен
• line_profiler
• hotshot
• gprof2dot
• memory_profiler
• objgraph
• memprof
• для django есть миддлвары с картинками и
графиками
• django debug toolbar
• django live profiler
• …
Profiling.
‹#›
Задача: профилирование живого WEB-сервера
• мы не хотим чтобы профилировщик значительно снижал
производительность
• мы хотим получить более-менее репрезентативные данные
Решение:
1. поднять апстрим на ~1% и собирать статистику с него (*)
2. воспроизвести на стейджинге/тестовом окружении
Альтернатива:
• настраиваем access logs
• смотрим, где медленно
• разбираемся почему
Итого.
‹#›
• проводим серию испытаний
• замеряем среднее время
• исключаем I/O, профилировщик и тп
• помним про погрешность
• разогреваем JIT (* PyPy ~ 0.2c — см. доки)
• как-то используем результаты теста, иначе JIT может
его «вырезать»
• целевой пробег сопоставим по производительности с
разогревочным
• целевой пробег на JIT должен работать быстрее
Как правильно писать тесты на производительность?
‹#›
• Регрессионные тесты
• Не нужно делать гипотез и предположений: только
цифры
• Проблему с I/O исключили
• Первое что стоит оптимизировать — алгоритм
• Проблема скорее всего в каком-то из циклов
• Все статические переменные должны быть вынесены
из цикла
• eval, exec — плохо
• Не увлекаться!
О чем всегда помнить
‹#›
CPython — интерпретатор.
Он честно интерпретирует каждую строку кода.
• Lookup-ы — очень дороги
• атрибуты и методы
• локальные/глобальные переменные
• замыкание
• Запоминание переменных дорого
• Создание объектов — дорого
• Изменение размеров объектов в памяти — дорого
• eval, exec — плохо
Особенности присущие CPython
‹#›
PyPy использует JIT.
PyPy пытается исполнить то, что вы имели в виду
Исполняется совсем не тот код, который вы пишите.
• JIT scope != trace: locals(), globals(), sys._getframe(),
sys.exc_info(), sys.settrace, …
• На JIT компиляцию требуется время (>0.2s)
• => то, что «гоняется редко» — оптимизировано не
будет
• C-модули поддерживаются плохо: используем Python-
версию
• eval, exec — плохо
Особенности присущие PyPy
‹#›
ПРИМЕРЫ
‹#›
FizzBuzz
Для данного списка натуральный чисел (int) вернуть
строку со значениями через запятую, где
• числа, делящиеся на 3 заменены на "Fizz";
• числа, делящиеся на 5 заменены на "Buzz";
• числа, делящиеся одновременно и на 3, и на 5
заменены на "FizzBuzz";
• остальные числа выведены как есть.
Например:
[1, 2, 5, 15, 3, 1, 1, 4] => "1,2,Buzz,FizzBuzz,Fizz,1,1,4"
http://rosettacode.org/wiki/FizzBuzz
‹#›
FizzBuzz. Самое простое решение (Гуглим).
for i in xrange(1, 101):

if i % 15 == 0:

print "FizzBuzz"

elif i % 3 == 0:

print "Fizz"

elif i % 5 == 0:

print "Buzz"

else:

print i
‹#›
FizzBuzz. Самое простое решение.
def fizzbuzz_simple(arr):

output_array = []

for i in arr:

if i % 15 == 0:

output_array.append("FizzBuzz")

elif i % 3 == 0:

output_array.append("Fizz")

elif i % 5 == 0:

output_array.append("Buzz")

else:

output_array.append(str(i))

return ",".join(output_array)
‹#›
FizzBuzz: Тесты
CORRECT_100 = (

"1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,"

"16,17,Fizz,19,Buzz,Fizz,22,23,Fizz,Buzz,26,Fizz,28,29,FizzBuzz,"

"31,32,Fizz,34,Buzz,Fizz,37,38,Fizz,Buzz,41,Fizz,43,44,FizzBuzz,"

"46,47,Fizz,49,Buzz,Fizz,52,53,Fizz,Buzz,56,Fizz,58,59,FizzBuzz,"

"61,62,Fizz,64,Buzz,Fizz,67,68,Fizz,Buzz,71,Fizz,73,74,FizzBuzz,"

"76,77,Fizz,79,Buzz,Fizz,82,83,Fizz,Buzz,86,Fizz,88,89,FizzBuzz,"

"91,92,Fizz,94,Buzz,Fizz,97,98,Fizz,Buzz"

)
def check_correct_100(fn):

print 'checking function {fn.__name__}'.format(**locals()),

output = fn(range(1, 101))

if output == CORRECT_100:

print '.. ok'

else:

print ‘.. failed'
‹#›
FizzBuzz: Тайминг
import gc
import hashlib
import time
from random import shuffle





def _timetest(fn, n):

gc.disable()

gc.collect()



setup = [range(1, 101) for _ in xrange(n)]

map(shuffle, setup)

ts = time.clock()

output = map(fn, setup)

tt = time.clock() - ts

print '.. took {:.5f}s, for {} runs, avg={}ms hash={}'.format(

tt, n, tt * 1000 / n, hashlib.md5(''.join(output)).hexdigest())

gc.enable()


def check_time_taken(fn, n_warming=10000, n_executing=1000):

print 'checking function {fn.__name__} for speed'.format(**locals())

print 'warming up',

_timetest(fn, n_warming)



print 'executing',

_timetest(fn, n_executing)
‹#›
Инструменты
• Юнит-тесты или иной способ проверки правильности алгоритма
check_correct_100(fizzbuzz_simple)
• Замеры времени
check_time_taken(fizzbuzz_simple)
• Модуль dis
from dis import dis

dis(fizzbuzz_simple)
• Модуль Profile
from profile import run

run('fizzbuzz_simple(range(100000))')
• Утилита Pycallgraph
from pycallgraph import PyCallGraph

from pycallgraph.output import GraphvizOutput



with PyCallGraph(output=GraphvizOutput()):

fizzbuzz_simple(range(100000))
‹#›
Как выглядит вывод dis
4 0 BUILD_LIST 0
3 STORE_FAST 1 (output_array)
5 6 SETUP_LOOP 129 (to 138)
9 LOAD_FAST 0 (arr)
12 GET_ITER
>> 13 FOR_ITER 121 (to 137)
16 STORE_FAST 2 (i)
6 19 LOAD_FAST 2 (i)
22 LOAD_CONST 1 (15)
25 BINARY_MODULO
26 LOAD_CONST 2 (0)
29 COMPARE_OP 2 (==)
32 POP_JUMP_IF_FALSE 51
7 35 LOAD_FAST 1 (output_array)
38 LOAD_ATTR 0 (append)
41 LOAD_CONST 3 ('FizzBuzz')
44 CALL_FUNCTION 1
47 POP_TOP
48 JUMP_ABSOLUTE 13
8 >> 51 LOAD_FAST 2 (i)
54 LOAD_CONST 4 (3)
57 BINARY_MODULO
58 LOAD_CONST 2 (0)
61 COMPARE_OP 2 (==)
64 POP_JUMP_IF_FALSE 83
9 67 LOAD_FAST 1 (output_array)
70 LOAD_ATTR 0 (append)
73 LOAD_CONST 5 ('Fizz')
76 CALL_FUNCTION 1
79 POP_TOP
80 JUMP_ABSOLUTE 13
10 >> 83 LOAD_FAST 2 (i)
86 LOAD_CONST 6 (5)
89 BINARY_MODULO
90 LOAD_CONST 2 (0)
93 COMPARE_OP 2 (==)
96 POP_JUMP_IF_FALSE 115
11 99 LOAD_FAST 1 (output_array)
102 LOAD_ATTR 0 (append)
105 LOAD_CONST 7 ('Buzz')
108 CALL_FUNCTION 1
111 POP_TOP
112 JUMP_ABSOLUTE 13
13 >> 115 LOAD_FAST 1 (output_array)
118 LOAD_ATTR 0 (append)
121 LOAD_GLOBAL 1 (str)
124 LOAD_FAST 2 (i)
127 CALL_FUNCTION 1
130 CALL_FUNCTION 1
133 POP_TOP
134 JUMP_ABSOLUTE 13
>> 137 POP_BLOCK
14 >> 138 LOAD_CONST 8 (',')
141 LOAD_ATTR 2 (join)
144 LOAD_FAST 1 (output_array)
147 CALL_FUNCTION 1
150 RETURN_VALUE
4 0 BUILD_LIST 0
3 STORE_FAST 1 (output_array)
5 6 SETUP_LOOP 129 (to 138)
9 LOAD_FAST 0 (arr)
12 GET_ITER
>> 13 FOR_ITER 121 (to 137)
16 STORE_FAST 2 (i)
6 19 LOAD_FAST 2 (i)
22 LOAD_CONST 1 (15)
25 BINARY_MODULO
26 LOAD_CONST 2 (0)
29 COMPARE_OP 2 (==)
32 POP_JUMP_IF_FALSE 51
7 35 LOAD_FAST 1 (output_array)
38 LOAD_ATTR 0 (append)
41 LOAD_CONST 3 ('FizzBuzz')
44 CALL_FUNCTION 1
47 POP_TOP
48 JUMP_ABSOLUTE 13
. . .
‹#›
Как выглядит вывод профайлера
100006 function calls in 0.699 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
100000 0.302 0.000 0.302 0.000 :0(append)
1 0.003 0.003 0.003 0.003 :0(join)
1 0.003 0.003 0.003 0.003 :0(range)
1 0.002 0.002 0.002 0.002 :0(setprofile)
1 0.002 0.002 0.697 0.697 <string>:1(<module>)
1 0.388 0.388 0.692 0.692 example_1_profile.py:3(fizzbuzz_simple)
1 0.000 0.000 0.699 0.699 profile:0(fizzbuzz_simple(range(100000)))
0 0.000 0.000 profile:0(profiler)
‹#›
Как выглядит вывод профайлера
100006 function calls in 0.699 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
100000 0.302 0.000 0.302 0.000 :0(append)
1 0.003 0.003 0.003 0.003 :0(join)
1 0.003 0.003 0.003 0.003 :0(range)
1 0.002 0.002 0.002 0.002 :0(setprofile)
1 0.002 0.002 0.697 0.697 <string>:1(<module>)
1 0.388 0.388 0.692 0.692 example_1_profile.py:3(fizzbuzz_simple)
1 0.000 0.000 0.699 0.699 profile:0(fizzbuzz_simple(range(100000)))
0 0.000 0.000 profile:0(profiler)
Проблемный
участок
‹#›
Как выглядит вывод профайлера
100006 function calls in 0.699 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
100000 0.302 0.000 0.302 0.000 :0(append)
1 0.003 0.003 0.003 0.003 :0(join)
1 0.003 0.003 0.003 0.003 :0(range)
1 0.002 0.002 0.002 0.002 :0(setprofile)
1 0.002 0.002 0.697 0.697 <string>:1(<module>)
1 0.388 0.388 0.692 0.692 example_1_profile.py:3(fizzbuzz_simple)
1 0.000 0.000 0.699 0.699 profile:0(fizzbuzz_simple(range(100000)))
0 0.000 0.000 profile:0(profiler)
Артефакт
‹#›
Как выглядит вывод PyCallGraph
‹#›
FizzBuzz. eval.
def fizzbuzz_simple(arr):

output_array = []

for i in arr:

if i % 15 == 0:

eval(
'output_array.append("FizzBuzz")',
globals(), locals())

elif i % 3 == 0:

output_array.append("Fizz")

elif i % 5 == 0:

output_array.append("Buzz")

else:

output_array.append(str(i))

return ",".join(output_array)
‹#›
FizzBuzz. exec.
def fizzbuzz_simple(arr):

output_array = []

for i in arr:

if i % 15 == 0:

(exec
‘output_array.append("FizzBuzz")')

elif i % 3 == 0:

output_array.append("Fizz")

elif i % 5 == 0:

output_array.append("Buzz")

else:

output_array.append(str(i))

return ",".join(output_array)
‹#›
FizzBuzz: OOP.
ArrayProcessor
Replacer
ItemProcessor
Array of items
Replacer
Replacer
processed as string
‹#›
FizzBuzz: OOP.
class AbstractReplacer(object):

__metaclass__ = ABCMeta

__slots__ = 'value', 'output'

return_value = NotImplemented



def __init__(self, value):

pass



@abstractmethod

def validate_input(self):

raise NotImplementedError



@abstractmethod

def check_match(self):

raise NotImplementedError



@abstractmethod

def process(self):

raise NotImplementedError



@abstractmethod

def get_output_value(self):

raise NotImplementedError

‹#›
FizzBuzz: OOP.
class AbstractItemProcessor(object):

__metaclass__ = ABCMeta

__slots__ = 'value', 'output'

replacer_classes = NotImplemented



def __init__(self, value):

pass



@abstractmethod

def validate_input(self):

raise NotImplementedError



@abstractmethod

def validate_processed_value(self):

raise NotImplementedError



@abstractmethod

def process(self):

raise NotImplementedError



@abstractmethod

def get_replacer_classes(self):

raise NotImplementedError



@abstractmethod

def get_output_value(self):

raise NotImplementedError

‹#›
FizzBuzz: OOP.
class AbstractArrayProcessor(object):

__metaclass__ = ABCMeta

__slots__ = 'array', 'output'

item_processer_class = NotImplemented



def __init__(self, array):

pass



@abstractmethod

def validate_input(self):

raise NotImplementedError



@abstractmethod

def process(self):

raise NotImplementedError



@abstractmethod

def get_item_processer_class(self):

raise NotImplementedError



@abstractmethod

def get_output_value(self):

raise NotImplementedError

‹#›
FizzBuzz: OOP.
class ImproperInputValue(Exception):

pass





class ImproperOutputValue(Exception):

pass

‹#›
FizzBuzz: OOP.
class BaseReplacer(AbstractReplacer):

return_value = None

divider = 1



def __init__(self, value):

super(BaseReplacer, self).__init__(value)

self.value = value

self.validate_input()

self.output = None



def validate_input(self):

if not isinstance(self.value, int):

raise ImproperInputValue(self.value)



def check_match(self):

return self.value % self.divider == 0



def process(self):

if self.check_match():

self.output = self.return_value



def get_output_value(self):

return self.output



‹#›
FizzBuzz: OOP.
class BaseItemProcessor(AbstractItemProcessor):

replacer_classes = BaseReplacer,



def __init__(self, value):

super(BaseItemProcesser, self).__init__(value)

self.value = value

self.validate_input()

self.output = None



def validate_input(self):

if not isinstance(self.value, int):

raise ImproperInputValue(self.value)



def validate_processed_value(self):

if not isinstance(self.output, basestring):

raise ImproperOutputValue



def process(self):

for replacer_class in self.get_replacer_classes():

replacer = replacer_class(self.value)

replacer.process()

processed_value = replacer.get_output_value()

if processed_value is not None:

self.output = processed_value

break



def get_replacer_classes(self):

return self.replacer_classes



def get_output_value(self):

return self.output

‹#›
FizzBuzz: OOP.
class BaseArrayProcessor(AbstractArrayProcessor):

item_processor_class = BaseItemProcessor



def __init__(self, array):

super(BaseArrayProcessor, self).__init__(array)

self.array = array

self.validate_input()

self.output = ''



def validate_input(self):

if not isinstance(self.array, (list, tuple, set)):

raise ImproperInputValue(self.array)



def process(self):

output_array = []

for item in self.array:

item_processor_class = self.get_item_processor_class()

item_processor = item_processor_class(item)

item_processor.process()

processed_item = item_processor.get_output_value()

if processed_item:

output_array.append(processed_item)

self.output = ','.join(output_array)



def get_item_processor_class(self):

return self.item_processor_class



def get_output_value(self):

return self.output



‹#›
FizzBuzz: OOP.
FIZZ = "Fizz"

BUZZ = "Buzz"

FIZZBUZZ = FIZZ + BUZZ





class MultiplesOfThreeReplacer(BaseReplacer):

return_value = FIZZ

divider = 3





class MultiplesOfFiveReplacer(BaseReplacer):

return_value = BUZZ

divider = 5





class MultiplesOfThreeAndFiveReplacer(BaseReplacer):

return_value = FIZZBUZZ

divider = 15





class IntToStrReplacer(BaseReplacer):

def check_match(self):

return True



def process(self):

self.output = str(self.value)



‹#›
FizzBuzz: OOP.


class FizzBuzzItemProcessor(BaseItemProcessor):

replacer_classes = (

MultiplesOfThreeAndFiveReplacer,

MultiplesOfThreeReplacer,

MultiplesOfFiveReplacer,

IntToStrReplacer,

)





class FizzBuzzProcessor(BaseArrayProcessor):

item_processor_class = FizzBuzzItemProcessor





def fizzbuzz_oop(arr):

fbp = FizzBuzzProcessor(arr)

fbp.process()

return fbp.get_output_value()

‹#›
ЗАМЕРЫ
‹#›
FizzBuzz: Результаты
cpython pypy cpython
to FizzBuzz
OOP
cpython
pypy
to FizzBuzz
OOP
cpythonFizzBuzz
OOP
24,11218 1х
FizzBuzz
simple
Adding eval
Adding exec
FizzBuzz
optimized
‹#›
FizzBuzz: Результаты
cpython pypy cpython
to FizzBuzz
OOP
cpython
pypy
to FizzBuzz
OOP
cpythonFizzBuzz
OOP
24,11218 0,72933 1х 33x
FizzBuzz
simple
Adding eval
Adding exec
FizzBuzz
optimized
‹#›
FizzBuzz: Результаты
cpython pypy cpython
to FizzBuzz
OOP
cpython
pypy
to FizzBuzz
OOP
cpythonFizzBuzz
OOP
24,11218 0,72933 1х 33x
FizzBuzz
simple
1,23326 0,23751 19,5х 101х
Adding eval
Adding exec
FizzBuzz
optimized
‹#›
FizzBuzz: Результаты
cpython pypy cpython
to FizzBuzz
OOP
cpython
pypy
to FizzBuzz
OOP
cpythonFizzBuzz
OOP
24,11218 0,72933 1х 33x
FizzBuzz
simple
1,23326 0,23751 19,5х 101х
Adding eval 3,49037 6,34854 6,9х 3,8x
Adding exec
FizzBuzz
optimized
‹#›
FizzBuzz: Результаты
cpython pypy cpython
to FizzBuzz
OOP
cpython
pypy
to FizzBuzz
OOP
cpythonFizzBuzz
OOP
24,11218 0,72933 1х 33x
FizzBuzz
simple
1,23326 0,23751 19,5х 101х
Adding eval 3,49037 6,34854 6,9х 3,8x
Adding exec 3,90273 — 6х —
FizzBuzz
optimized
‹#›
FizzBuzz: Результаты
cpython pypy cpython
to FizzBuzz
OOP
cpython
pypy
to FizzBuzz
OOP
cpythonFizzBuzz
OOP
24,11218 0,72933 1х 33x
FizzBuzz
simple
1,23326 0,23751 19,5х 101х
Adding eval 3,49037 6,34854 6,9х 3,8x
Adding exec 3,90273 — 6х —
FizzBuzz
optimized
? ? ? ?
‹#›
FizzBuzz: OOP. PyCallGraph
‹#›
FizzBuzz: OOP. PyCallGraph
Самые ресурсоемкие вызовы
‹#›
О ПРЕЖДЕВРЕМЕННОЙ ОПТИМИЗАЦИИ
‹#›
Оптимизация алгоритма
Для данного списка натуральный чисел (int) вернуть
строку со значениями через запятую, где
• числа, делящиеся на 3 заменены на "Fizz";
• числа, делящиеся на 5 заменены на "Buzz";
• числа, делящиеся одновременно и на 3, и на 5
заменены на "FizzBuzz";
• остальные числа выведены как есть.
Например:
[1, 2, 5, 15, 3, 1, 1, 4] => "1,2,Buzz,FizzBuzz,Fizz,1,1,4"
http://rosettacode.org/wiki/FizzBuzz
‹#›
Оптимизация алгоритма
def fizzbuzz_simple(arr):

output_array = []

for i in arr:

if i % 15 == 0:

output_array.append("FizzBuzz")

elif i % 3 == 0:

output_array.append("Fizz")

elif i % 5 == 0:

output_array.append("Buzz")

else:

output_array.append(str(i))

return ",".join(output_array)
15?
‹#›
Оптимизация алгоритма
def fizzbuzz_simple(arr):

output_array = []

for i in arr:

if i % 3 == 0 and i % 5 == 0:

output_array.append("FizzBuzz")

elif i % 3 == 0:

output_array.append("Fizz")

elif i % 5 == 0:

output_array.append("Buzz")

else:

output_array.append(str(i))

return ",".join(output_array)
‹#›
Оптимизация алгоритма
def fizzbuzz_simple(arr):

output_array = []

for i in arr:

if i % 3 == 0 and i % 5 == 0:

output_array.append("FizzBuzz")

elif i % 3 == 0:

output_array.append("Fizz")

elif i % 5 == 0:

output_array.append("Buzz")

else:

output_array.append(str(i))

return ",".join(output_array)
‹#›
Оптимизация алгоритма
def fizzbuzz_simple(arr):

output_array = []

for i in arr:
if i % 3 == 0:

if i % 5 == 0:

output_array.append("FizzBuzz")

else:

output_array.append("Fizz")

elif i % 5 == 0:

output_array.append("Buzz")

else:

output_array.append(str(i))

return ",".join(output_array)
‹#›
Оптимизация алгоритма
Количество сравнений для списка значений 1 .. 15
До … 39
После … 30
По времени ~ 3% разницы
По количеству операций ~ 30%
А что если переставить порядок сравнений?
‹#›
Оптимизация алгоритма. Перестановка операций
def fizzbuzz_simple(arr):

output_array = []

for i in arr:

if i % 15 == 0:

output_array.append("FizzBuzz")

elif i % 5 == 0:

output_array.append("Buzz")

elif i % 3 == 0:

output_array.append("Fizz")

else:

output_array.append(str(i))

return ",".join(output_array)
‹#›
Оптимизация алгоритма. Перестановка операций
def fizzbuzz_simple(arr):

output_array = []

for i in arr:
if i % 5 == 0:

if i % 3 == 0:

output_array.append("FizzBuzz")

else:

output_array.append("Buzz")

elif i % 3 == 0:

output_array.append("Fizz")

else:

output_array.append(str(i))

return ",".join(output_array)
‹#›
Оптимизация алгоритма. Перестановка операций
Количество сравнений для списка значений 1 .. 15
Плохой вариант
До … 39
После … 41 (хуже)
Улучшенный вариант
До … 30
После … 30 (не изменилось)
От лучшего до худшего ~ 30%
‹#›
ОПТИМИЗИРУЕМ CPYTHON
‹#›
FizzBuzz: Результаты
cpython pypy cpython
to FizzBuzz
OOP
cpython
pypy
to FizzBuzz
OOP
cpythonFizzBuzz
OOP
24,11218 0,72933 1х 33x
FizzBuzz
simple
1,23326 0,23751 19,5х 101х
Adding eval 3,49037 6,34854 6,9х 3,8x
Adding exec 3,90273 — 6х —
FizzBuzz
optimized
? ? ? ?
‹#›
Оптимизируем CPython. Lookup
def fizzbuzz_simple(arr):

output_array = []

for i in arr:
if i % 5 == 0:

if i % 3 == 0:

output_array.append("FizzBuzz")

else:

output_array.append("Buzz")

elif i % 3 == 0:

output_array.append("Fizz")

else:

output_array.append(str(i))

return ",".join(output_array)
‹#›
Оптимизируем CPython. Lookup
def fizzbuzz_simple(arr):

output_array = []
_append = output_array.append

for i in arr:
if i % 5 == 0:

if i % 3 == 0:

_append(«FizzBuzz")

else:

_append(«Buzz")

elif i % 3 == 0:

_append(«Fizz")

else:

_append(str(i))

return ",".join(output_array)
‹#›
Оптимизируем CPython. Lookup
def fizzbuzz_simple(arr):

output_array = []
_append = output_array.append

for i in arr:
if i % 5 == 0:

if i % 3 == 0:

_append(«FizzBuzz")

else:

_append(«Buzz")

elif i % 3 == 0:

_append(«Fizz")

else:

_append(str(i))

return ",".join(output_array) 1.3x
‹#›
FizzBuzz: Тесты
CORRECT_100 = (

"1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,"

"16,17,Fizz,19,Buzz,Fizz,22,23,Fizz,Buzz,26,Fizz,28,29,FizzBuzz,"

"31,32,Fizz,34,Buzz,Fizz,37,38,Fizz,Buzz,41,Fizz,43,44,FizzBuzz,"

"46,47,Fizz,49,Buzz,Fizz,52,53,Fizz,Buzz,56,Fizz,58,59,FizzBuzz,"

"61,62,Fizz,64,Buzz,Fizz,67,68,Fizz,Buzz,71,Fizz,73,74,FizzBuzz,"

"76,77,Fizz,79,Buzz,Fizz,82,83,Fizz,Buzz,86,Fizz,88,89,FizzBuzz,"

"91,92,Fizz,94,Buzz,Fizz,97,98,Fizz,Buzz"

)
def check_correct_100(fn):

print 'checking function {fn.__name__}'.format(**locals()),

output = fn(range(1, 101))

if output == CORRECT_100:

print '.. ok'

else:

print ‘.. failed'
‹#›
Быстрый FizzBuzz
def fizzbuzz_samples_helper(arr):

for i in arr:

if i % 3 == 0:

if i % 5 == 0:

yield "FizzBuzz"

else:

yield "Fizz"

elif i % 5 == 0:

yield "Buzz"

else:

yield False





samples = tuple(fizzbuzz_samples_helper(xrange(15)))
‹#›
FizzBuzz. Перестановка операций
samples = (False, False, «Fizz" ,False,
«Buzz", . . . , "FizzBuzz")
def fizzbuzz(arr):

output_array = [
samples[i % 15] or str(i) for i in arr]

return ",".join(output_array)
‹#›
FizzBuzz. Перестановка операций
samples = (False, False, «Fizz" ,False,
«Buzz", . . . , "FizzBuzz")
def fizzbuzz(arr):

output_array = [
samples[i % 15] or str(i) for i in arr]

return ",".join(output_array)
1,35x
‹#›
Быстрый FizzBuzz


def fizzbuzz_with_precached_samples(

arr,

# shorteners
__join=",".join, 

__samples=samples,

__str=str

):

return __join(__samples[i % 15] or __str(i) for i in arr)
‹#›
Быстрый FizzBuzz


def fizzbuzz_with_precached_samples(

arr,

# shorteners
__join=",".join, 

__samples=samples,

__str=str

):

return __join(__samples[i % 15] or __str(i) for i in arr)
0,96x ?
‹#›
FizzBuzz: Результаты
cpython pypy cpython
to FizzBuzz
OOP
cpython
pypy
to FizzBuzz
OOP
cpythonFizzBuzz
OOP
24,11218 0,72933 1х 33x
FizzBuzz
simple
1,23326 0,23751 19,5х 101х
Adding eval 3,49037 6,34854 6,9х 3,8x
Adding exec 3,90273 — 6х —
FizzBuzz
optimized
0,72047 0,24492 33,4x 101x
‹#›
ПРОДВИНУТЫЕ ПОДХОДЫ
‹#›
СОПРОЦЕСС / COROUTINE
‹#›
Coroutines
64 0 LOAD_FAST 1 (__join)
3 LOAD_CLOSURE 0 (__samples)
6 LOAD_CLOSURE 1 (__str)
9 BUILD_TUPLE 2
12 LOAD_CONST 1 (<code object
<genexpr> at 0x10d849930, file "./___.py", line 64>)
15 MAKE_CLOSURE 0
18 LOAD_FAST 0 (arr)
21 GET_ITER
22 CALL_FUNCTION 1
25 CALL_FUNCTION 1
28 RETURN_VALUE
‹#›
Coroutines
def fizzbuzz_co(

# shorteners

__join=",".join,

__samples=samples,

__str=str

):

arr = ()

while True:

arr = yield __join(__samples[i % 15] or __str(i) for i in arr)



_ = fizzbuzz_co()

_.next()

fizzbuzz_co= _.send
‹#›
Coroutines
def fizzbuzz_co(

# shorteners

__join=",".join,

__samples=samples,

__str=str

):

arr = ()

while True:

arr = yield __join(__samples[i % 15] or __str(i) for i in arr)





_ = fizzbuzz_co()

_.next()

fizzbuzz_co= _.send
outputinput
output = co.send(input)
«инициализировать» и
получить первый output
создать сопроцесс
заменить ссылку на метод send
‹#›
def co():
. . .
x = yield y
[return None]
c = co()
out = c.send(Z)
Coroutines
Как это работает
• def + yield = ключевые слова
• создаем «конструктор» генератора
• вызов c = co() создает генератор c
• c.next()
• выполнит все до первого yield,
• вернет результат выражения y,
• «встанет на паузу»
• c.send(Z)
• x = Z
• продолжит выполнение до yield/return
• out = y
• return завершает выполнение (StopIteration)
‹#›
Coroutines
Можно обернуть в декоратор:


def coroutine(fn):

_ = fn()

_.next()

return _.send
‹#›
Coroutines
… и поместить все внутрь (до первого yield)
@coroutine

def fizzbuzz_co():

def fizzbuzz_samples_helper(arr):

for i in arr:

if i % 3 == 0:

if i % 5 == 0:

yield "FizzBuzz"

else:

yield "Fizz"

elif i % 5 == 0:

yield "Buzz"

else:

yield False



__join = ",".join

__str = str

samples = tuple(fizzbuzz_samples_helper(xrange(15)))

arr = ()



while True:

arr = yield __join(samples[i % 15] or __str(i) for i in arr)
‹#›
КЕШИРУЮЩИЕ ФУНКЦИИ
‹#›
Быстрый FizzBuzz, кэширующая функция
Кэширующая функция
• вычисления ресурсоемки
• значения аргументов часто повторяются
def cached(fn):

cache = {}



@wraps(fn)

def decorated(arg):

value = cache.get(arg)

if not value:

cache[arg] = value = fn(arg)

return value



return decorated
‹#›
Быстрый FizzBuzz, кэширующая функция
@cached

def process_one(

i,

# shorteners

__samples=samples,

__str=str

):

return __samples[i % 15] or __str(i)

def fizzbuzz_with_cache(

arr,

# shorteners

__join=",".join,

):

return __join(map(process_one, arr))
‹#›
COROUTINE-BASED CLASS
‹#›
class MakeSum(Exception):

pass





class ChgKoef(Exception):

pass





def co():

x = None

y = None

k = 1

rv = None



while True:

try:

x, y = yield rv

rv = k * x * y

except MakeSum as e:

x, y = e.args

rv = k * (x + y)

except ChgKoef as e:

k = e.args[0]
Coroutine based class
class Cls(object):

def __init__(self):

self.x = None

self.y = None

self.k = 1

self.rv = None



def main_method(self, x, y):

self.x = x

self.y = y

self.rv = self.k * self.x * self.y

return self.rv



def make_sum(self, x, y):

self.x = x

self.y = y

self.rv = self.k * (self.x + self.y)

return self.rv



def chg_koef(self, k):

self.k = k

return self.rv
‹#›
instance = co()

print instance

# <generator object co at 0x10047bbe0>



print instance.next()

# None



print instance.send((1, 2))

# 2 == 1 * 1 * 2



print instance.send((3, 4))

# 12 == 1 * 3 * 4



print instance.throw(MakeSum(5, 6))

# 11 == 1 * (5 + 6)



print instance.send((7, 8))

# 56 == 1 * 7 * 8



print instance.throw(ChgKoef(10))

# 56 (last value repeated)



print instance.send((1, 2))

# 20 == 10 * 1 * 2
Coroutine based inheritance
instance = Cls()

print instance

# <__main__.Cls object at 0x10a1c6210>



print instance.main_method(1, 2)

# 2 == 1 * 1 * 2



print instance.main_method(3, 4)

# 12 == 1 * 3 * 4



print instance.make_sum(5, 6)

# 11 == 1 * (5 + 6)



print instance.main_method(7, 8)

# 56 == 1 * 7 * 8



print instance.chg_koef(10)

# 56 (last value repeated)



print instance.main_method(1, 2)

# 20 == 10 * 1 * 2
‹#›
COROUTINE-BASED INHERITANCE
‹#›
def co(

param,

# ___

__some_value=5,

__some_method=lambda: 10

):

rv = None



while True:

input = yield rv



def co_sub(

param,

# ___

__some_value=10,

__some_method=lambda: 20

):

return co(**locals())
Coroutine based class
class Cls(object):

def __init__(self, param):

self.param = param



some_value = 5



def some_method(self):

return 10



def main_method(self):

return





class SubCls(Cls):

some_value = 10



def some_method(self):

return 20
‹#›
Coroutine based class
coroutine class coroutine vs
class
send
main method
4,23 6,93 1,63x faster
throw MakeSum
make_sum
21,85 7,30 3x slower
‹#›
Coroutine based class
Плюсы
• Основной метод работает быстрее
• «Наследование»
Минусы
• Интерфейс «заморожен»
• Основной метод «заморожен»
• Код «специфичен»
‹#›
«ЧИСЛОДРОБИЛКИ»
‹#›
Cython, numpy, weave, etc..
«Числодробилки»
Travis Oliphant
from numpy import zeros

from scipy import weave



dx = 0.1

dy = 0.1

dx2 = dx*dx

dy2 = dy*dy



def py_update(u):

nx, ny = u.shape

for i in xrange(1,nx-1):

for j in xrange(1, ny-1):

u[i,j] = ((u[i+1, j] + u[i-1, j]) * dy2 +

(u[i, j+1] + u[i, j-1]) * dx2) / (2*(dx2+dy2))



def calc(N, Niter=100, func=py_update, args=()):

u = zeros([N, N])

u[0] = 1

for i in range(Niter):

func(u,*args)

return u
‹#›
Почти тот же Python!


cimport numpy as np



def cy_update(np.ndarray[double, ndim=2] u, double dx2, double dy2):

cdef unsigned int i, j

for i in xrange(1,u.shape[0]-1):

for j in xrange(1, u.shape[1]-1):

u[i,j] = ((u[i+1, j] + u[i-1, j]) * dy2 +

(u[i, j+1] + u[i, j-1]) * dx2) / (2*(dx2+dy2))

Cython, numpy, weave, etc..
‹#›
Cython, numpy, weave, etc..
Почти «чистый С»
def weave_update(u):

code = """

int i, j;

for (i=1; i<Nu[0]-1; i++) {

for (j=1; j<Nu[1]-1; j++) {

U2(i,j) = ((U2(i+1, j) + U2(i-1, j))*dy2 + 

(U2(i, j+1) + U2(i, j-1))*dx2) / (2*(dx2+dy2));

}

}

"""

weave.inline(code, ['u', 'dx2', 'dy2'])
‹#›
Cython, numpy, weave, etc..
Method Time (sec) relative speed
(меньше-лучше)
Pure python 560 250
NumPy 2,24 1
Cython 1,28 0,51
Weave 1,02 0,45
Faster Cython 0,94 0,42
‹#›
РЕЦЕПТ
‹#›
Рецепт
• найти слабое место
• убедиться что все упирается в производительность кода, а не в
дисковое/сетевое IO
• упростить ООП до простых функций и процедур
• оптимизировать алгоритм
• избавиться от лишних переменных
• избавиться от конструкций object.method()
• использовать итераторы/генераторы вместо списков
• завернуть все в сопроцессы
• постоянно замерять производительность на данных, схожих с
реальными
• тестировать
• знать когда остановиться
‹#›
• Ссылки, литература:
• Дэвид Бизли: генераторы/сопроцессы http://www.dabeaz.com/generators/
• Python и память http://www.slideshare.net/PiotrPrzymus/pprzymus-europython-2014
• Другой пример о профилировали — числа фибоначчи http://pymotw.com/2/profile/
• Про объекты, ссылки и утечки памяти http://mg.pov.lt/objgraph/
• line_profiler, memory_profiler http://www.huyng.com/posts/python-
performance-analysis/
• numpy, cython, weave http://technicaldiscovery.blogspot.ru/2011/06/speeding-up-
python-numpy-cython-and.html
• google
• Контакты:
• email: iremizov@parallels.com #CodeFest
• twitter: @iremizov

Mais conteúdo relacionado

Mais procurados

ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...Alexey Paznikov
 
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...Alexey Paznikov
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksMikhail Kurnosov
 
Something about Golang
Something about GolangSomething about Golang
Something about GolangAnton Arhipov
 
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Mikhail Kurnosov
 
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...Alexey Paznikov
 
Developing highload servers with Java
Developing highload servers with JavaDeveloping highload servers with Java
Developing highload servers with JavaAndrei Pangin
 
Модель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, ЯндексМодель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, ЯндексYandex
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...Alexey Paznikov
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPMikhail Kurnosov
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Python Meetup
 
Распространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийРаспространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийMikhail Shcherbakov
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...Alexey Paznikov
 
Александр Фокин, Рефлексия в C++
Александр Фокин, Рефлексия в C++Александр Фокин, Рефлексия в C++
Александр Фокин, Рефлексия в C++Sergey Platonov
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Sergey Platonov
 
Поговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложенийПоговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложенийAndrey Akinshin
 
Для чего мы делали свой акторный фреймворк и что из этого вышло?
Для чего мы делали свой акторный фреймворк и что из этого вышло?Для чего мы делали свой акторный фреймворк и что из этого вышло?
Для чего мы делали свой акторный фреймворк и что из этого вышло?Yauheni Akhotnikau
 
Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о BoostSergey Platonov
 
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программированияПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программированияAlexey Paznikov
 
Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.Roman Brovko
 

Mais procurados (20)

ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
 
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
 
Something about Golang
Something about GolangSomething about Golang
Something about Golang
 
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
 
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
 
Developing highload servers with Java
Developing highload servers with JavaDeveloping highload servers with Java
Developing highload servers with Java
 
Модель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, ЯндексМодель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, Яндекс
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMP
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Распространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийРаспространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложений
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
 
Александр Фокин, Рефлексия в C++
Александр Фокин, Рефлексия в C++Александр Фокин, Рефлексия в C++
Александр Фокин, Рефлексия в C++
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
Поговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложенийПоговорим о микрооптимизациях .NET-приложений
Поговорим о микрооптимизациях .NET-приложений
 
Для чего мы делали свой акторный фреймворк и что из этого вышло?
Для чего мы делали свой акторный фреймворк и что из этого вышло?Для чего мы делали свой акторный фреймворк и что из этого вышло?
Для чего мы делали свой акторный фреймворк и что из этого вышло?
 
Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о Boost
 
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программированияПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
 
Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.
 

Semelhante a Сверхоптимизация кода на Python

Основы и применение статического анализа кода при разработке лекция 1
Основы и применение статического анализа кода при разработке лекция 1Основы и применение статического анализа кода при разработке лекция 1
Основы и применение статического анализа кода при разработке лекция 1m2rus
 
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНСit-people
 
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Yandex
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioAndrey Karpov
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMTech Talks @NSU
 
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Platonov Sergey
 
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...Yandex
 
Опыт разработки статического анализатора кода
Опыт разработки статического анализатора кодаОпыт разработки статического анализатора кода
Опыт разработки статического анализатора кодаAndrey Karpov
 
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»Platonov Sergey
 
Денис Колодин: Low-latency и soft-realtime на Python
Денис Колодин: Low-latency и soft-realtime на PythonДенис Колодин: Low-latency и soft-realtime на Python
Денис Колодин: Low-latency и soft-realtime на Pythonit-people
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кодаAndrey Karpov
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода Pavel Tsukanov
 
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"Fwdays
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыcorehard_by
 
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru GroupКак не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru GroupMail.ru Group
 
Web осень 2013 лекция 9
Web осень 2013 лекция 9Web осень 2013 лекция 9
Web осень 2013 лекция 9Technopark
 
PostgreSQL performance recipes
PostgreSQL performance recipesPostgreSQL performance recipes
PostgreSQL performance recipesAlexey Ermakov
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3etyumentcev
 

Semelhante a Сверхоптимизация кода на Python (20)

Python и Cython
Python и CythonPython и Cython
Python и Cython
 
Основы и применение статического анализа кода при разработке лекция 1
Основы и применение статического анализа кода при разработке лекция 1Основы и применение статического анализа кода при разработке лекция 1
Основы и применение статического анализа кода при разработке лекция 1
 
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
 
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-Studio
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVM
 
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
 
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
 
Опыт разработки статического анализатора кода
Опыт разработки статического анализатора кодаОпыт разработки статического анализатора кода
Опыт разработки статического анализатора кода
 
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
 
Денис Колодин: Low-latency и soft-realtime на Python
Денис Колодин: Low-latency и soft-realtime на PythonДенис Колодин: Low-latency и soft-realtime на Python
Денис Колодин: Low-latency и soft-realtime на Python
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтеры
 
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru GroupКак не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
 
Web осень 2013 лекция 9
Web осень 2013 лекция 9Web осень 2013 лекция 9
Web осень 2013 лекция 9
 
PostgreSQL performance recipes
PostgreSQL performance recipesPostgreSQL performance recipes
PostgreSQL performance recipes
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
 

Mais de ru_Parallels

OpenStack лучше с Virtuozzo
OpenStack лучше с VirtuozzoOpenStack лучше с Virtuozzo
OpenStack лучше с Virtuozzoru_Parallels
 
Расширяем горизонты с помощью iCloud
Расширяем горизонты с помощью iCloudРасширяем горизонты с помощью iCloud
Расширяем горизонты с помощью iCloudru_Parallels
 
Цифровой суверенитет для российских облачных систем
Цифровой суверенитет для российских облачных системЦифровой суверенитет для российских облачных систем
Цифровой суверенитет для российских облачных системru_Parallels
 
Виртуализация инфраструктуры ЦОД российской разработки
Виртуализация инфраструктуры ЦОД российской разработкиВиртуализация инфраструктуры ЦОД российской разработки
Виртуализация инфраструктуры ЦОД российской разработкиru_Parallels
 
Resource management in the cloud
Resource management in the cloudResource management in the cloud
Resource management in the cloudru_Parallels
 
Тестирование ПО, основанного на сторонних компонентах, на примере дистрибут...
Тестирование ПО, основанного на  сторонних компонентах, на примере  дистрибут...Тестирование ПО, основанного на  сторонних компонентах, на примере  дистрибут...
Тестирование ПО, основанного на сторонних компонентах, на примере дистрибут...ru_Parallels
 
Передача состояния с iPhone на Apple Watch
Передача состояния с iPhone на Apple WatchПередача состояния с iPhone на Apple Watch
Передача состояния с iPhone на Apple Watchru_Parallels
 
Надежность ПО и Runtime Verification
Надежность ПО и Runtime VerificationНадежность ПО и Runtime Verification
Надежность ПО и Runtime Verificationru_Parallels
 
Vagrant Plugin development
Vagrant Plugin developmentVagrant Plugin development
Vagrant Plugin developmentru_Parallels
 
Управление рисками в разработке программного обеспечения
Управление рисками в разработке программного обеспеченияУправление рисками в разработке программного обеспечения
Управление рисками в разработке программного обеспеченияru_Parallels
 
Virtuozzo platform
Virtuozzo platformVirtuozzo platform
Virtuozzo platformru_Parallels
 
Что должен уметь Linux программист
Что должен уметь Linux программистЧто должен уметь Linux программист
Что должен уметь Linux программистru_Parallels
 
Доклад Михаила Егорова на PHDays
Доклад Михаила Егорова на PHDaysДоклад Михаила Егорова на PHDays
Доклад Михаила Егорова на PHDaysru_Parallels
 
Доклад Якова Зубарева на конференции MBLT15
Доклад Якова Зубарева на конференции MBLT15Доклад Якова Зубарева на конференции MBLT15
Доклад Якова Зубарева на конференции MBLT15ru_Parallels
 
2013 WHD.local Istanbul Presentation
2013 WHD.local Istanbul Presentation2013 WHD.local Istanbul Presentation
2013 WHD.local Istanbul Presentationru_Parallels
 
Инновационные решения для роста облачного бизнеса
Инновационные решения для роста облачного бизнесаИнновационные решения для роста облачного бизнеса
Инновационные решения для роста облачного бизнесаru_Parallels
 
Kolerov parallels cloud_market_in_numbers
Kolerov parallels cloud_market_in_numbersKolerov parallels cloud_market_in_numbers
Kolerov parallels cloud_market_in_numbersru_Parallels
 
Pavel Ershov on WHD.Moscow
Pavel Ershov on WHD.MoscowPavel Ershov on WHD.Moscow
Pavel Ershov on WHD.Moscowru_Parallels
 
KAnisimov riw2011-hosting-future
KAnisimov riw2011-hosting-futureKAnisimov riw2011-hosting-future
KAnisimov riw2011-hosting-futureru_Parallels
 

Mais de ru_Parallels (19)

OpenStack лучше с Virtuozzo
OpenStack лучше с VirtuozzoOpenStack лучше с Virtuozzo
OpenStack лучше с Virtuozzo
 
Расширяем горизонты с помощью iCloud
Расширяем горизонты с помощью iCloudРасширяем горизонты с помощью iCloud
Расширяем горизонты с помощью iCloud
 
Цифровой суверенитет для российских облачных систем
Цифровой суверенитет для российских облачных системЦифровой суверенитет для российских облачных систем
Цифровой суверенитет для российских облачных систем
 
Виртуализация инфраструктуры ЦОД российской разработки
Виртуализация инфраструктуры ЦОД российской разработкиВиртуализация инфраструктуры ЦОД российской разработки
Виртуализация инфраструктуры ЦОД российской разработки
 
Resource management in the cloud
Resource management in the cloudResource management in the cloud
Resource management in the cloud
 
Тестирование ПО, основанного на сторонних компонентах, на примере дистрибут...
Тестирование ПО, основанного на  сторонних компонентах, на примере  дистрибут...Тестирование ПО, основанного на  сторонних компонентах, на примере  дистрибут...
Тестирование ПО, основанного на сторонних компонентах, на примере дистрибут...
 
Передача состояния с iPhone на Apple Watch
Передача состояния с iPhone на Apple WatchПередача состояния с iPhone на Apple Watch
Передача состояния с iPhone на Apple Watch
 
Надежность ПО и Runtime Verification
Надежность ПО и Runtime VerificationНадежность ПО и Runtime Verification
Надежность ПО и Runtime Verification
 
Vagrant Plugin development
Vagrant Plugin developmentVagrant Plugin development
Vagrant Plugin development
 
Управление рисками в разработке программного обеспечения
Управление рисками в разработке программного обеспеченияУправление рисками в разработке программного обеспечения
Управление рисками в разработке программного обеспечения
 
Virtuozzo platform
Virtuozzo platformVirtuozzo platform
Virtuozzo platform
 
Что должен уметь Linux программист
Что должен уметь Linux программистЧто должен уметь Linux программист
Что должен уметь Linux программист
 
Доклад Михаила Егорова на PHDays
Доклад Михаила Егорова на PHDaysДоклад Михаила Егорова на PHDays
Доклад Михаила Егорова на PHDays
 
Доклад Якова Зубарева на конференции MBLT15
Доклад Якова Зубарева на конференции MBLT15Доклад Якова Зубарева на конференции MBLT15
Доклад Якова Зубарева на конференции MBLT15
 
2013 WHD.local Istanbul Presentation
2013 WHD.local Istanbul Presentation2013 WHD.local Istanbul Presentation
2013 WHD.local Istanbul Presentation
 
Инновационные решения для роста облачного бизнеса
Инновационные решения для роста облачного бизнесаИнновационные решения для роста облачного бизнеса
Инновационные решения для роста облачного бизнеса
 
Kolerov parallels cloud_market_in_numbers
Kolerov parallels cloud_market_in_numbersKolerov parallels cloud_market_in_numbers
Kolerov parallels cloud_market_in_numbers
 
Pavel Ershov on WHD.Moscow
Pavel Ershov on WHD.MoscowPavel Ershov on WHD.Moscow
Pavel Ershov on WHD.Moscow
 
KAnisimov riw2011-hosting-future
KAnisimov riw2011-hosting-futureKAnisimov riw2011-hosting-future
KAnisimov riw2011-hosting-future
 

Сверхоптимизация кода на Python

  • 2. ‹#› Картинка для привлечения внимания * CPython, без привлечения внешних зависимостей, компиляции и тп 30x
  • 3.
  • 4. ‹#› Python Рассматриваем язык: Python 2.x Конкретные реализации: CPython (*nix default) PyPy (JIT)
  • 5. ‹#› Проблемы приложения. Какие проблемы вообще бывают? • неудачные архитектурные решения • неудачно выбранные компоненты и фреймворки • медленный I/O • высокий расход памяти, утечки памяти • медленный код
  • 6. ‹#› Проблемы приложения. Как решается большинство проблем? • добавление воркеров • кеширование • отложенные задания, очереди • замена компонентов • map/reduce • изменение архитектуры • …
  • 7. ‹#› Когда это критично и не решаемо «привычными» способами? Обработка потоковых данных пример: процессинг датчиков (акселерометры, гироскопы) Десериализация пример: JSON, pickle, .. Авторегрессия пример: EMA (скользящая средняя), численное интегрирование, ряды Стейт-машины пример: AI, синтаксические анализаторы текста Медленный код.
  • 8. ‹#› Профилирование специальными утилитами • ручной профайлинг (тайминг) • статистический профайлинг (сэмплинг) • событийный профайлинг (граф вызовов) Логгирование и сбор статистики • настройка конфигов apache/nginx/… • логи приложения Как найти критические участки кода?
  • 9. ‹#› Утилиты • profile/cprofile • pycallgraph • dis (иногда бывает полезно) Profiling.
  • 10. ‹#› Выбор огромен • line_profiler • hotshot • gprof2dot • memory_profiler • objgraph • memprof • для django есть миддлвары с картинками и графиками • django debug toolbar • django live profiler • … Profiling.
  • 11. ‹#› Задача: профилирование живого WEB-сервера • мы не хотим чтобы профилировщик значительно снижал производительность • мы хотим получить более-менее репрезентативные данные Решение: 1. поднять апстрим на ~1% и собирать статистику с него (*) 2. воспроизвести на стейджинге/тестовом окружении Альтернатива: • настраиваем access logs • смотрим, где медленно • разбираемся почему Итого.
  • 12. ‹#› • проводим серию испытаний • замеряем среднее время • исключаем I/O, профилировщик и тп • помним про погрешность • разогреваем JIT (* PyPy ~ 0.2c — см. доки) • как-то используем результаты теста, иначе JIT может его «вырезать» • целевой пробег сопоставим по производительности с разогревочным • целевой пробег на JIT должен работать быстрее Как правильно писать тесты на производительность?
  • 13. ‹#› • Регрессионные тесты • Не нужно делать гипотез и предположений: только цифры • Проблему с I/O исключили • Первое что стоит оптимизировать — алгоритм • Проблема скорее всего в каком-то из циклов • Все статические переменные должны быть вынесены из цикла • eval, exec — плохо • Не увлекаться! О чем всегда помнить
  • 14. ‹#› CPython — интерпретатор. Он честно интерпретирует каждую строку кода. • Lookup-ы — очень дороги • атрибуты и методы • локальные/глобальные переменные • замыкание • Запоминание переменных дорого • Создание объектов — дорого • Изменение размеров объектов в памяти — дорого • eval, exec — плохо Особенности присущие CPython
  • 15. ‹#› PyPy использует JIT. PyPy пытается исполнить то, что вы имели в виду Исполняется совсем не тот код, который вы пишите. • JIT scope != trace: locals(), globals(), sys._getframe(), sys.exc_info(), sys.settrace, … • На JIT компиляцию требуется время (>0.2s) • => то, что «гоняется редко» — оптимизировано не будет • C-модули поддерживаются плохо: используем Python- версию • eval, exec — плохо Особенности присущие PyPy
  • 17. ‹#› FizzBuzz Для данного списка натуральный чисел (int) вернуть строку со значениями через запятую, где • числа, делящиеся на 3 заменены на "Fizz"; • числа, делящиеся на 5 заменены на "Buzz"; • числа, делящиеся одновременно и на 3, и на 5 заменены на "FizzBuzz"; • остальные числа выведены как есть. Например: [1, 2, 5, 15, 3, 1, 1, 4] => "1,2,Buzz,FizzBuzz,Fizz,1,1,4" http://rosettacode.org/wiki/FizzBuzz
  • 18. ‹#› FizzBuzz. Самое простое решение (Гуглим). for i in xrange(1, 101):
 if i % 15 == 0:
 print "FizzBuzz"
 elif i % 3 == 0:
 print "Fizz"
 elif i % 5 == 0:
 print "Buzz"
 else:
 print i
  • 19. ‹#› FizzBuzz. Самое простое решение. def fizzbuzz_simple(arr):
 output_array = []
 for i in arr:
 if i % 15 == 0:
 output_array.append("FizzBuzz")
 elif i % 3 == 0:
 output_array.append("Fizz")
 elif i % 5 == 0:
 output_array.append("Buzz")
 else:
 output_array.append(str(i))
 return ",".join(output_array)
  • 20. ‹#› FizzBuzz: Тесты CORRECT_100 = (
 "1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,"
 "16,17,Fizz,19,Buzz,Fizz,22,23,Fizz,Buzz,26,Fizz,28,29,FizzBuzz,"
 "31,32,Fizz,34,Buzz,Fizz,37,38,Fizz,Buzz,41,Fizz,43,44,FizzBuzz,"
 "46,47,Fizz,49,Buzz,Fizz,52,53,Fizz,Buzz,56,Fizz,58,59,FizzBuzz,"
 "61,62,Fizz,64,Buzz,Fizz,67,68,Fizz,Buzz,71,Fizz,73,74,FizzBuzz,"
 "76,77,Fizz,79,Buzz,Fizz,82,83,Fizz,Buzz,86,Fizz,88,89,FizzBuzz,"
 "91,92,Fizz,94,Buzz,Fizz,97,98,Fizz,Buzz"
 ) def check_correct_100(fn):
 print 'checking function {fn.__name__}'.format(**locals()),
 output = fn(range(1, 101))
 if output == CORRECT_100:
 print '.. ok'
 else:
 print ‘.. failed'
  • 21. ‹#› FizzBuzz: Тайминг import gc import hashlib import time from random import shuffle
 
 
 def _timetest(fn, n):
 gc.disable()
 gc.collect()
 
 setup = [range(1, 101) for _ in xrange(n)]
 map(shuffle, setup)
 ts = time.clock()
 output = map(fn, setup)
 tt = time.clock() - ts
 print '.. took {:.5f}s, for {} runs, avg={}ms hash={}'.format(
 tt, n, tt * 1000 / n, hashlib.md5(''.join(output)).hexdigest())
 gc.enable() 
 def check_time_taken(fn, n_warming=10000, n_executing=1000):
 print 'checking function {fn.__name__} for speed'.format(**locals())
 print 'warming up',
 _timetest(fn, n_warming)
 
 print 'executing',
 _timetest(fn, n_executing)
  • 22. ‹#› Инструменты • Юнит-тесты или иной способ проверки правильности алгоритма check_correct_100(fizzbuzz_simple) • Замеры времени check_time_taken(fizzbuzz_simple) • Модуль dis from dis import dis
 dis(fizzbuzz_simple) • Модуль Profile from profile import run
 run('fizzbuzz_simple(range(100000))') • Утилита Pycallgraph from pycallgraph import PyCallGraph
 from pycallgraph.output import GraphvizOutput
 
 with PyCallGraph(output=GraphvizOutput()):
 fizzbuzz_simple(range(100000))
  • 23. ‹#› Как выглядит вывод dis 4 0 BUILD_LIST 0 3 STORE_FAST 1 (output_array) 5 6 SETUP_LOOP 129 (to 138) 9 LOAD_FAST 0 (arr) 12 GET_ITER >> 13 FOR_ITER 121 (to 137) 16 STORE_FAST 2 (i) 6 19 LOAD_FAST 2 (i) 22 LOAD_CONST 1 (15) 25 BINARY_MODULO 26 LOAD_CONST 2 (0) 29 COMPARE_OP 2 (==) 32 POP_JUMP_IF_FALSE 51 7 35 LOAD_FAST 1 (output_array) 38 LOAD_ATTR 0 (append) 41 LOAD_CONST 3 ('FizzBuzz') 44 CALL_FUNCTION 1 47 POP_TOP 48 JUMP_ABSOLUTE 13 8 >> 51 LOAD_FAST 2 (i) 54 LOAD_CONST 4 (3) 57 BINARY_MODULO 58 LOAD_CONST 2 (0) 61 COMPARE_OP 2 (==) 64 POP_JUMP_IF_FALSE 83 9 67 LOAD_FAST 1 (output_array) 70 LOAD_ATTR 0 (append) 73 LOAD_CONST 5 ('Fizz') 76 CALL_FUNCTION 1 79 POP_TOP 80 JUMP_ABSOLUTE 13 10 >> 83 LOAD_FAST 2 (i) 86 LOAD_CONST 6 (5) 89 BINARY_MODULO 90 LOAD_CONST 2 (0) 93 COMPARE_OP 2 (==) 96 POP_JUMP_IF_FALSE 115 11 99 LOAD_FAST 1 (output_array) 102 LOAD_ATTR 0 (append) 105 LOAD_CONST 7 ('Buzz') 108 CALL_FUNCTION 1 111 POP_TOP 112 JUMP_ABSOLUTE 13 13 >> 115 LOAD_FAST 1 (output_array) 118 LOAD_ATTR 0 (append) 121 LOAD_GLOBAL 1 (str) 124 LOAD_FAST 2 (i) 127 CALL_FUNCTION 1 130 CALL_FUNCTION 1 133 POP_TOP 134 JUMP_ABSOLUTE 13 >> 137 POP_BLOCK 14 >> 138 LOAD_CONST 8 (',') 141 LOAD_ATTR 2 (join) 144 LOAD_FAST 1 (output_array) 147 CALL_FUNCTION 1 150 RETURN_VALUE 4 0 BUILD_LIST 0 3 STORE_FAST 1 (output_array) 5 6 SETUP_LOOP 129 (to 138) 9 LOAD_FAST 0 (arr) 12 GET_ITER >> 13 FOR_ITER 121 (to 137) 16 STORE_FAST 2 (i) 6 19 LOAD_FAST 2 (i) 22 LOAD_CONST 1 (15) 25 BINARY_MODULO 26 LOAD_CONST 2 (0) 29 COMPARE_OP 2 (==) 32 POP_JUMP_IF_FALSE 51 7 35 LOAD_FAST 1 (output_array) 38 LOAD_ATTR 0 (append) 41 LOAD_CONST 3 ('FizzBuzz') 44 CALL_FUNCTION 1 47 POP_TOP 48 JUMP_ABSOLUTE 13 . . .
  • 24. ‹#› Как выглядит вывод профайлера 100006 function calls in 0.699 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 100000 0.302 0.000 0.302 0.000 :0(append) 1 0.003 0.003 0.003 0.003 :0(join) 1 0.003 0.003 0.003 0.003 :0(range) 1 0.002 0.002 0.002 0.002 :0(setprofile) 1 0.002 0.002 0.697 0.697 <string>:1(<module>) 1 0.388 0.388 0.692 0.692 example_1_profile.py:3(fizzbuzz_simple) 1 0.000 0.000 0.699 0.699 profile:0(fizzbuzz_simple(range(100000))) 0 0.000 0.000 profile:0(profiler)
  • 25. ‹#› Как выглядит вывод профайлера 100006 function calls in 0.699 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 100000 0.302 0.000 0.302 0.000 :0(append) 1 0.003 0.003 0.003 0.003 :0(join) 1 0.003 0.003 0.003 0.003 :0(range) 1 0.002 0.002 0.002 0.002 :0(setprofile) 1 0.002 0.002 0.697 0.697 <string>:1(<module>) 1 0.388 0.388 0.692 0.692 example_1_profile.py:3(fizzbuzz_simple) 1 0.000 0.000 0.699 0.699 profile:0(fizzbuzz_simple(range(100000))) 0 0.000 0.000 profile:0(profiler) Проблемный участок
  • 26. ‹#› Как выглядит вывод профайлера 100006 function calls in 0.699 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 100000 0.302 0.000 0.302 0.000 :0(append) 1 0.003 0.003 0.003 0.003 :0(join) 1 0.003 0.003 0.003 0.003 :0(range) 1 0.002 0.002 0.002 0.002 :0(setprofile) 1 0.002 0.002 0.697 0.697 <string>:1(<module>) 1 0.388 0.388 0.692 0.692 example_1_profile.py:3(fizzbuzz_simple) 1 0.000 0.000 0.699 0.699 profile:0(fizzbuzz_simple(range(100000))) 0 0.000 0.000 profile:0(profiler) Артефакт
  • 28. ‹#› FizzBuzz. eval. def fizzbuzz_simple(arr):
 output_array = []
 for i in arr:
 if i % 15 == 0:
 eval( 'output_array.append("FizzBuzz")', globals(), locals())
 elif i % 3 == 0:
 output_array.append("Fizz")
 elif i % 5 == 0:
 output_array.append("Buzz")
 else:
 output_array.append(str(i))
 return ",".join(output_array)
  • 29. ‹#› FizzBuzz. exec. def fizzbuzz_simple(arr):
 output_array = []
 for i in arr:
 if i % 15 == 0:
 (exec ‘output_array.append("FizzBuzz")')
 elif i % 3 == 0:
 output_array.append("Fizz")
 elif i % 5 == 0:
 output_array.append("Buzz")
 else:
 output_array.append(str(i))
 return ",".join(output_array)
  • 30. ‹#› FizzBuzz: OOP. ArrayProcessor Replacer ItemProcessor Array of items Replacer Replacer processed as string
  • 31. ‹#› FizzBuzz: OOP. class AbstractReplacer(object):
 __metaclass__ = ABCMeta
 __slots__ = 'value', 'output'
 return_value = NotImplemented
 
 def __init__(self, value):
 pass
 
 @abstractmethod
 def validate_input(self):
 raise NotImplementedError
 
 @abstractmethod
 def check_match(self):
 raise NotImplementedError
 
 @abstractmethod
 def process(self):
 raise NotImplementedError
 
 @abstractmethod
 def get_output_value(self):
 raise NotImplementedError

  • 32. ‹#› FizzBuzz: OOP. class AbstractItemProcessor(object):
 __metaclass__ = ABCMeta
 __slots__ = 'value', 'output'
 replacer_classes = NotImplemented
 
 def __init__(self, value):
 pass
 
 @abstractmethod
 def validate_input(self):
 raise NotImplementedError
 
 @abstractmethod
 def validate_processed_value(self):
 raise NotImplementedError
 
 @abstractmethod
 def process(self):
 raise NotImplementedError
 
 @abstractmethod
 def get_replacer_classes(self):
 raise NotImplementedError
 
 @abstractmethod
 def get_output_value(self):
 raise NotImplementedError

  • 33. ‹#› FizzBuzz: OOP. class AbstractArrayProcessor(object):
 __metaclass__ = ABCMeta
 __slots__ = 'array', 'output'
 item_processer_class = NotImplemented
 
 def __init__(self, array):
 pass
 
 @abstractmethod
 def validate_input(self):
 raise NotImplementedError
 
 @abstractmethod
 def process(self):
 raise NotImplementedError
 
 @abstractmethod
 def get_item_processer_class(self):
 raise NotImplementedError
 
 @abstractmethod
 def get_output_value(self):
 raise NotImplementedError

  • 35. ‹#› FizzBuzz: OOP. class BaseReplacer(AbstractReplacer):
 return_value = None
 divider = 1
 
 def __init__(self, value):
 super(BaseReplacer, self).__init__(value)
 self.value = value
 self.validate_input()
 self.output = None
 
 def validate_input(self):
 if not isinstance(self.value, int):
 raise ImproperInputValue(self.value)
 
 def check_match(self):
 return self.value % self.divider == 0
 
 def process(self):
 if self.check_match():
 self.output = self.return_value
 
 def get_output_value(self):
 return self.output
 

  • 36. ‹#› FizzBuzz: OOP. class BaseItemProcessor(AbstractItemProcessor):
 replacer_classes = BaseReplacer,
 
 def __init__(self, value):
 super(BaseItemProcesser, self).__init__(value)
 self.value = value
 self.validate_input()
 self.output = None
 
 def validate_input(self):
 if not isinstance(self.value, int):
 raise ImproperInputValue(self.value)
 
 def validate_processed_value(self):
 if not isinstance(self.output, basestring):
 raise ImproperOutputValue
 
 def process(self):
 for replacer_class in self.get_replacer_classes():
 replacer = replacer_class(self.value)
 replacer.process()
 processed_value = replacer.get_output_value()
 if processed_value is not None:
 self.output = processed_value
 break
 
 def get_replacer_classes(self):
 return self.replacer_classes
 
 def get_output_value(self):
 return self.output

  • 37. ‹#› FizzBuzz: OOP. class BaseArrayProcessor(AbstractArrayProcessor):
 item_processor_class = BaseItemProcessor
 
 def __init__(self, array):
 super(BaseArrayProcessor, self).__init__(array)
 self.array = array
 self.validate_input()
 self.output = ''
 
 def validate_input(self):
 if not isinstance(self.array, (list, tuple, set)):
 raise ImproperInputValue(self.array)
 
 def process(self):
 output_array = []
 for item in self.array:
 item_processor_class = self.get_item_processor_class()
 item_processor = item_processor_class(item)
 item_processor.process()
 processed_item = item_processor.get_output_value()
 if processed_item:
 output_array.append(processed_item)
 self.output = ','.join(output_array)
 
 def get_item_processor_class(self):
 return self.item_processor_class
 
 def get_output_value(self):
 return self.output
 

  • 38. ‹#› FizzBuzz: OOP. FIZZ = "Fizz"
 BUZZ = "Buzz"
 FIZZBUZZ = FIZZ + BUZZ
 
 
 class MultiplesOfThreeReplacer(BaseReplacer):
 return_value = FIZZ
 divider = 3
 
 
 class MultiplesOfFiveReplacer(BaseReplacer):
 return_value = BUZZ
 divider = 5
 
 
 class MultiplesOfThreeAndFiveReplacer(BaseReplacer):
 return_value = FIZZBUZZ
 divider = 15
 
 
 class IntToStrReplacer(BaseReplacer):
 def check_match(self):
 return True
 
 def process(self):
 self.output = str(self.value)
 

  • 39. ‹#› FizzBuzz: OOP. 
 class FizzBuzzItemProcessor(BaseItemProcessor):
 replacer_classes = (
 MultiplesOfThreeAndFiveReplacer,
 MultiplesOfThreeReplacer,
 MultiplesOfFiveReplacer,
 IntToStrReplacer,
 )
 
 
 class FizzBuzzProcessor(BaseArrayProcessor):
 item_processor_class = FizzBuzzItemProcessor
 
 
 def fizzbuzz_oop(arr):
 fbp = FizzBuzzProcessor(arr)
 fbp.process()
 return fbp.get_output_value()

  • 41. ‹#› FizzBuzz: Результаты cpython pypy cpython to FizzBuzz OOP cpython pypy to FizzBuzz OOP cpythonFizzBuzz OOP 24,11218 1х FizzBuzz simple Adding eval Adding exec FizzBuzz optimized
  • 42. ‹#› FizzBuzz: Результаты cpython pypy cpython to FizzBuzz OOP cpython pypy to FizzBuzz OOP cpythonFizzBuzz OOP 24,11218 0,72933 1х 33x FizzBuzz simple Adding eval Adding exec FizzBuzz optimized
  • 43. ‹#› FizzBuzz: Результаты cpython pypy cpython to FizzBuzz OOP cpython pypy to FizzBuzz OOP cpythonFizzBuzz OOP 24,11218 0,72933 1х 33x FizzBuzz simple 1,23326 0,23751 19,5х 101х Adding eval Adding exec FizzBuzz optimized
  • 44. ‹#› FizzBuzz: Результаты cpython pypy cpython to FizzBuzz OOP cpython pypy to FizzBuzz OOP cpythonFizzBuzz OOP 24,11218 0,72933 1х 33x FizzBuzz simple 1,23326 0,23751 19,5х 101х Adding eval 3,49037 6,34854 6,9х 3,8x Adding exec FizzBuzz optimized
  • 45. ‹#› FizzBuzz: Результаты cpython pypy cpython to FizzBuzz OOP cpython pypy to FizzBuzz OOP cpythonFizzBuzz OOP 24,11218 0,72933 1х 33x FizzBuzz simple 1,23326 0,23751 19,5х 101х Adding eval 3,49037 6,34854 6,9х 3,8x Adding exec 3,90273 — 6х — FizzBuzz optimized
  • 46. ‹#› FizzBuzz: Результаты cpython pypy cpython to FizzBuzz OOP cpython pypy to FizzBuzz OOP cpythonFizzBuzz OOP 24,11218 0,72933 1х 33x FizzBuzz simple 1,23326 0,23751 19,5х 101х Adding eval 3,49037 6,34854 6,9х 3,8x Adding exec 3,90273 — 6х — FizzBuzz optimized ? ? ? ?
  • 48. ‹#› FizzBuzz: OOP. PyCallGraph Самые ресурсоемкие вызовы
  • 50. ‹#› Оптимизация алгоритма Для данного списка натуральный чисел (int) вернуть строку со значениями через запятую, где • числа, делящиеся на 3 заменены на "Fizz"; • числа, делящиеся на 5 заменены на "Buzz"; • числа, делящиеся одновременно и на 3, и на 5 заменены на "FizzBuzz"; • остальные числа выведены как есть. Например: [1, 2, 5, 15, 3, 1, 1, 4] => "1,2,Buzz,FizzBuzz,Fizz,1,1,4" http://rosettacode.org/wiki/FizzBuzz
  • 51. ‹#› Оптимизация алгоритма def fizzbuzz_simple(arr):
 output_array = []
 for i in arr:
 if i % 15 == 0:
 output_array.append("FizzBuzz")
 elif i % 3 == 0:
 output_array.append("Fizz")
 elif i % 5 == 0:
 output_array.append("Buzz")
 else:
 output_array.append(str(i))
 return ",".join(output_array) 15?
  • 52. ‹#› Оптимизация алгоритма def fizzbuzz_simple(arr):
 output_array = []
 for i in arr:
 if i % 3 == 0 and i % 5 == 0:
 output_array.append("FizzBuzz")
 elif i % 3 == 0:
 output_array.append("Fizz")
 elif i % 5 == 0:
 output_array.append("Buzz")
 else:
 output_array.append(str(i))
 return ",".join(output_array)
  • 53. ‹#› Оптимизация алгоритма def fizzbuzz_simple(arr):
 output_array = []
 for i in arr:
 if i % 3 == 0 and i % 5 == 0:
 output_array.append("FizzBuzz")
 elif i % 3 == 0:
 output_array.append("Fizz")
 elif i % 5 == 0:
 output_array.append("Buzz")
 else:
 output_array.append(str(i))
 return ",".join(output_array)
  • 54. ‹#› Оптимизация алгоритма def fizzbuzz_simple(arr):
 output_array = []
 for i in arr: if i % 3 == 0:
 if i % 5 == 0:
 output_array.append("FizzBuzz")
 else:
 output_array.append("Fizz")
 elif i % 5 == 0:
 output_array.append("Buzz")
 else:
 output_array.append(str(i))
 return ",".join(output_array)
  • 55. ‹#› Оптимизация алгоритма Количество сравнений для списка значений 1 .. 15 До … 39 После … 30 По времени ~ 3% разницы По количеству операций ~ 30% А что если переставить порядок сравнений?
  • 56. ‹#› Оптимизация алгоритма. Перестановка операций def fizzbuzz_simple(arr):
 output_array = []
 for i in arr:
 if i % 15 == 0:
 output_array.append("FizzBuzz")
 elif i % 5 == 0:
 output_array.append("Buzz")
 elif i % 3 == 0:
 output_array.append("Fizz")
 else:
 output_array.append(str(i))
 return ",".join(output_array)
  • 57. ‹#› Оптимизация алгоритма. Перестановка операций def fizzbuzz_simple(arr):
 output_array = []
 for i in arr: if i % 5 == 0:
 if i % 3 == 0:
 output_array.append("FizzBuzz")
 else:
 output_array.append("Buzz")
 elif i % 3 == 0:
 output_array.append("Fizz")
 else:
 output_array.append(str(i))
 return ",".join(output_array)
  • 58. ‹#› Оптимизация алгоритма. Перестановка операций Количество сравнений для списка значений 1 .. 15 Плохой вариант До … 39 После … 41 (хуже) Улучшенный вариант До … 30 После … 30 (не изменилось) От лучшего до худшего ~ 30%
  • 60. ‹#› FizzBuzz: Результаты cpython pypy cpython to FizzBuzz OOP cpython pypy to FizzBuzz OOP cpythonFizzBuzz OOP 24,11218 0,72933 1х 33x FizzBuzz simple 1,23326 0,23751 19,5х 101х Adding eval 3,49037 6,34854 6,9х 3,8x Adding exec 3,90273 — 6х — FizzBuzz optimized ? ? ? ?
  • 61. ‹#› Оптимизируем CPython. Lookup def fizzbuzz_simple(arr):
 output_array = []
 for i in arr: if i % 5 == 0:
 if i % 3 == 0:
 output_array.append("FizzBuzz")
 else:
 output_array.append("Buzz")
 elif i % 3 == 0:
 output_array.append("Fizz")
 else:
 output_array.append(str(i))
 return ",".join(output_array)
  • 62. ‹#› Оптимизируем CPython. Lookup def fizzbuzz_simple(arr):
 output_array = [] _append = output_array.append
 for i in arr: if i % 5 == 0:
 if i % 3 == 0:
 _append(«FizzBuzz")
 else:
 _append(«Buzz")
 elif i % 3 == 0:
 _append(«Fizz")
 else:
 _append(str(i))
 return ",".join(output_array)
  • 63. ‹#› Оптимизируем CPython. Lookup def fizzbuzz_simple(arr):
 output_array = [] _append = output_array.append
 for i in arr: if i % 5 == 0:
 if i % 3 == 0:
 _append(«FizzBuzz")
 else:
 _append(«Buzz")
 elif i % 3 == 0:
 _append(«Fizz")
 else:
 _append(str(i))
 return ",".join(output_array) 1.3x
  • 64. ‹#› FizzBuzz: Тесты CORRECT_100 = (
 "1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,"
 "16,17,Fizz,19,Buzz,Fizz,22,23,Fizz,Buzz,26,Fizz,28,29,FizzBuzz,"
 "31,32,Fizz,34,Buzz,Fizz,37,38,Fizz,Buzz,41,Fizz,43,44,FizzBuzz,"
 "46,47,Fizz,49,Buzz,Fizz,52,53,Fizz,Buzz,56,Fizz,58,59,FizzBuzz,"
 "61,62,Fizz,64,Buzz,Fizz,67,68,Fizz,Buzz,71,Fizz,73,74,FizzBuzz,"
 "76,77,Fizz,79,Buzz,Fizz,82,83,Fizz,Buzz,86,Fizz,88,89,FizzBuzz,"
 "91,92,Fizz,94,Buzz,Fizz,97,98,Fizz,Buzz"
 ) def check_correct_100(fn):
 print 'checking function {fn.__name__}'.format(**locals()),
 output = fn(range(1, 101))
 if output == CORRECT_100:
 print '.. ok'
 else:
 print ‘.. failed'
  • 65. ‹#› Быстрый FizzBuzz def fizzbuzz_samples_helper(arr):
 for i in arr:
 if i % 3 == 0:
 if i % 5 == 0:
 yield "FizzBuzz"
 else:
 yield "Fizz"
 elif i % 5 == 0:
 yield "Buzz"
 else:
 yield False
 
 
 samples = tuple(fizzbuzz_samples_helper(xrange(15)))
  • 66. ‹#› FizzBuzz. Перестановка операций samples = (False, False, «Fizz" ,False, «Buzz", . . . , "FizzBuzz") def fizzbuzz(arr):
 output_array = [ samples[i % 15] or str(i) for i in arr]
 return ",".join(output_array)
  • 67. ‹#› FizzBuzz. Перестановка операций samples = (False, False, «Fizz" ,False, «Buzz", . . . , "FizzBuzz") def fizzbuzz(arr):
 output_array = [ samples[i % 15] or str(i) for i in arr]
 return ",".join(output_array) 1,35x
  • 68. ‹#› Быстрый FizzBuzz 
 def fizzbuzz_with_precached_samples(
 arr,
 # shorteners __join=",".join, 
 __samples=samples,
 __str=str
 ):
 return __join(__samples[i % 15] or __str(i) for i in arr)
  • 69. ‹#› Быстрый FizzBuzz 
 def fizzbuzz_with_precached_samples(
 arr,
 # shorteners __join=",".join, 
 __samples=samples,
 __str=str
 ):
 return __join(__samples[i % 15] or __str(i) for i in arr) 0,96x ?
  • 70. ‹#› FizzBuzz: Результаты cpython pypy cpython to FizzBuzz OOP cpython pypy to FizzBuzz OOP cpythonFizzBuzz OOP 24,11218 0,72933 1х 33x FizzBuzz simple 1,23326 0,23751 19,5х 101х Adding eval 3,49037 6,34854 6,9х 3,8x Adding exec 3,90273 — 6х — FizzBuzz optimized 0,72047 0,24492 33,4x 101x
  • 73. ‹#› Coroutines 64 0 LOAD_FAST 1 (__join) 3 LOAD_CLOSURE 0 (__samples) 6 LOAD_CLOSURE 1 (__str) 9 BUILD_TUPLE 2 12 LOAD_CONST 1 (<code object <genexpr> at 0x10d849930, file "./___.py", line 64>) 15 MAKE_CLOSURE 0 18 LOAD_FAST 0 (arr) 21 GET_ITER 22 CALL_FUNCTION 1 25 CALL_FUNCTION 1 28 RETURN_VALUE
  • 74. ‹#› Coroutines def fizzbuzz_co(
 # shorteners
 __join=",".join,
 __samples=samples,
 __str=str
 ):
 arr = ()
 while True:
 arr = yield __join(__samples[i % 15] or __str(i) for i in arr)
 
 _ = fizzbuzz_co()
 _.next()
 fizzbuzz_co= _.send
  • 75. ‹#› Coroutines def fizzbuzz_co(
 # shorteners
 __join=",".join,
 __samples=samples,
 __str=str
 ):
 arr = ()
 while True:
 arr = yield __join(__samples[i % 15] or __str(i) for i in arr)
 
 
 _ = fizzbuzz_co()
 _.next()
 fizzbuzz_co= _.send outputinput output = co.send(input) «инициализировать» и получить первый output создать сопроцесс заменить ссылку на метод send
  • 76. ‹#› def co(): . . . x = yield y [return None] c = co() out = c.send(Z) Coroutines Как это работает • def + yield = ключевые слова • создаем «конструктор» генератора • вызов c = co() создает генератор c • c.next() • выполнит все до первого yield, • вернет результат выражения y, • «встанет на паузу» • c.send(Z) • x = Z • продолжит выполнение до yield/return • out = y • return завершает выполнение (StopIteration)
  • 77. ‹#› Coroutines Можно обернуть в декоратор: 
 def coroutine(fn):
 _ = fn()
 _.next()
 return _.send
  • 78. ‹#› Coroutines … и поместить все внутрь (до первого yield) @coroutine
 def fizzbuzz_co():
 def fizzbuzz_samples_helper(arr):
 for i in arr:
 if i % 3 == 0:
 if i % 5 == 0:
 yield "FizzBuzz"
 else:
 yield "Fizz"
 elif i % 5 == 0:
 yield "Buzz"
 else:
 yield False
 
 __join = ",".join
 __str = str
 samples = tuple(fizzbuzz_samples_helper(xrange(15)))
 arr = ()
 
 while True:
 arr = yield __join(samples[i % 15] or __str(i) for i in arr)
  • 80. ‹#› Быстрый FizzBuzz, кэширующая функция Кэширующая функция • вычисления ресурсоемки • значения аргументов часто повторяются def cached(fn):
 cache = {}
 
 @wraps(fn)
 def decorated(arg):
 value = cache.get(arg)
 if not value:
 cache[arg] = value = fn(arg)
 return value
 
 return decorated
  • 81. ‹#› Быстрый FizzBuzz, кэширующая функция @cached
 def process_one(
 i,
 # shorteners
 __samples=samples,
 __str=str
 ):
 return __samples[i % 15] or __str(i)
 def fizzbuzz_with_cache(
 arr,
 # shorteners
 __join=",".join,
 ):
 return __join(map(process_one, arr))
  • 83. ‹#› class MakeSum(Exception):
 pass
 
 
 class ChgKoef(Exception):
 pass
 
 
 def co():
 x = None
 y = None
 k = 1
 rv = None
 
 while True:
 try:
 x, y = yield rv
 rv = k * x * y
 except MakeSum as e:
 x, y = e.args
 rv = k * (x + y)
 except ChgKoef as e:
 k = e.args[0] Coroutine based class class Cls(object):
 def __init__(self):
 self.x = None
 self.y = None
 self.k = 1
 self.rv = None
 
 def main_method(self, x, y):
 self.x = x
 self.y = y
 self.rv = self.k * self.x * self.y
 return self.rv
 
 def make_sum(self, x, y):
 self.x = x
 self.y = y
 self.rv = self.k * (self.x + self.y)
 return self.rv
 
 def chg_koef(self, k):
 self.k = k
 return self.rv
  • 84. ‹#› instance = co()
 print instance
 # <generator object co at 0x10047bbe0>
 
 print instance.next()
 # None
 
 print instance.send((1, 2))
 # 2 == 1 * 1 * 2
 
 print instance.send((3, 4))
 # 12 == 1 * 3 * 4
 
 print instance.throw(MakeSum(5, 6))
 # 11 == 1 * (5 + 6)
 
 print instance.send((7, 8))
 # 56 == 1 * 7 * 8
 
 print instance.throw(ChgKoef(10))
 # 56 (last value repeated)
 
 print instance.send((1, 2))
 # 20 == 10 * 1 * 2 Coroutine based inheritance instance = Cls()
 print instance
 # <__main__.Cls object at 0x10a1c6210>
 
 print instance.main_method(1, 2)
 # 2 == 1 * 1 * 2
 
 print instance.main_method(3, 4)
 # 12 == 1 * 3 * 4
 
 print instance.make_sum(5, 6)
 # 11 == 1 * (5 + 6)
 
 print instance.main_method(7, 8)
 # 56 == 1 * 7 * 8
 
 print instance.chg_koef(10)
 # 56 (last value repeated)
 
 print instance.main_method(1, 2)
 # 20 == 10 * 1 * 2
  • 86. ‹#› def co(
 param,
 # ___
 __some_value=5,
 __some_method=lambda: 10
 ):
 rv = None
 
 while True:
 input = yield rv
 
 def co_sub(
 param,
 # ___
 __some_value=10,
 __some_method=lambda: 20
 ):
 return co(**locals()) Coroutine based class class Cls(object):
 def __init__(self, param):
 self.param = param
 
 some_value = 5
 
 def some_method(self):
 return 10
 
 def main_method(self):
 return
 
 
 class SubCls(Cls):
 some_value = 10
 
 def some_method(self):
 return 20
  • 87. ‹#› Coroutine based class coroutine class coroutine vs class send main method 4,23 6,93 1,63x faster throw MakeSum make_sum 21,85 7,30 3x slower
  • 88. ‹#› Coroutine based class Плюсы • Основной метод работает быстрее • «Наследование» Минусы • Интерфейс «заморожен» • Основной метод «заморожен» • Код «специфичен»
  • 90. ‹#› Cython, numpy, weave, etc.. «Числодробилки» Travis Oliphant from numpy import zeros
 from scipy import weave
 
 dx = 0.1
 dy = 0.1
 dx2 = dx*dx
 dy2 = dy*dy
 
 def py_update(u):
 nx, ny = u.shape
 for i in xrange(1,nx-1):
 for j in xrange(1, ny-1):
 u[i,j] = ((u[i+1, j] + u[i-1, j]) * dy2 +
 (u[i, j+1] + u[i, j-1]) * dx2) / (2*(dx2+dy2))
 
 def calc(N, Niter=100, func=py_update, args=()):
 u = zeros([N, N])
 u[0] = 1
 for i in range(Niter):
 func(u,*args)
 return u
  • 91. ‹#› Почти тот же Python! 
 cimport numpy as np
 
 def cy_update(np.ndarray[double, ndim=2] u, double dx2, double dy2):
 cdef unsigned int i, j
 for i in xrange(1,u.shape[0]-1):
 for j in xrange(1, u.shape[1]-1):
 u[i,j] = ((u[i+1, j] + u[i-1, j]) * dy2 +
 (u[i, j+1] + u[i, j-1]) * dx2) / (2*(dx2+dy2))
 Cython, numpy, weave, etc..
  • 92. ‹#› Cython, numpy, weave, etc.. Почти «чистый С» def weave_update(u):
 code = """
 int i, j;
 for (i=1; i<Nu[0]-1; i++) {
 for (j=1; j<Nu[1]-1; j++) {
 U2(i,j) = ((U2(i+1, j) + U2(i-1, j))*dy2 + 
 (U2(i, j+1) + U2(i, j-1))*dx2) / (2*(dx2+dy2));
 }
 }
 """
 weave.inline(code, ['u', 'dx2', 'dy2'])
  • 93. ‹#› Cython, numpy, weave, etc.. Method Time (sec) relative speed (меньше-лучше) Pure python 560 250 NumPy 2,24 1 Cython 1,28 0,51 Weave 1,02 0,45 Faster Cython 0,94 0,42
  • 95. ‹#› Рецепт • найти слабое место • убедиться что все упирается в производительность кода, а не в дисковое/сетевое IO • упростить ООП до простых функций и процедур • оптимизировать алгоритм • избавиться от лишних переменных • избавиться от конструкций object.method() • использовать итераторы/генераторы вместо списков • завернуть все в сопроцессы • постоянно замерять производительность на данных, схожих с реальными • тестировать • знать когда остановиться
  • 96. ‹#› • Ссылки, литература: • Дэвид Бизли: генераторы/сопроцессы http://www.dabeaz.com/generators/ • Python и память http://www.slideshare.net/PiotrPrzymus/pprzymus-europython-2014 • Другой пример о профилировали — числа фибоначчи http://pymotw.com/2/profile/ • Про объекты, ссылки и утечки памяти http://mg.pov.lt/objgraph/ • line_profiler, memory_profiler http://www.huyng.com/posts/python- performance-analysis/ • numpy, cython, weave http://technicaldiscovery.blogspot.ru/2011/06/speeding-up- python-numpy-cython-and.html • google • Контакты: • email: iremizov@parallels.com #CodeFest • twitter: @iremizov