Upgrade to Pro — share decks privately, control downloads, hide ads and more …

“Микро-оптимизация кода на Python” - Андрей Тих...

Avatar for Quantori Quantori
November 14, 2021

“Микро-оптимизация кода на Python” - Андрей Тихонов

Андрей Тихонов, Lead Software Engineer.

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

Avatar for Quantori

Quantori

November 14, 2021
Tweet

More Decks by Quantori

Other Decks in Programming

Transcript

  1. quantori.com 2 Обо мне Андрей Тихонов Работал в Lamoda, Yandex,

    сейчас в Quantori Занимаюсь разработкой с 2009 года Писал на C, C++, Java, Golang, Python В телеграме - @Tishka17 (чаты @ru_python, @ru_python_beginners и др.)
  2. quantori.com 3 Микро-оптимизация кода на Python Что будет в докладе

    • Поговорим про автоматическую оптимизацию кода • Посмотрим на байткод CPython • Подвигаем код руками ради красивых графиков • Реализуем простенький оптимизатор на основе AST
  3. quantori.com 4 Микро-оптимизация кода на Python Оптимизация • Изменить алгоритм

    • Закэшировать неизменное • Отформатировать код • Надеяться на компилятор
  4. quantori.com 5 Микро-оптимизация кода на Python Автоматическая оптимизация Что используется?

    Исходный код Результаты профилирования Предварительно при компиляции Адаптивно в процессе работы Когда выполняется?
  5. quantori.com 6 Микро-оптимизация кода на Python Техники оптимизации компилятором •

    Оптимизация ветвей • Перемещение кода • Удаление общих подвыражений • Свёртка констант • Продвижение констант • Удаление мертвого кода • Удаление тупиковых записей • Встраивание функций • Раскрутка цикла
  6. quantori.com 8 Микро-оптимизация кода на Python Этапы выполнения кода на

    Python 1 5 3 2 4 Парсинг и токенизация текста Генерация графа потока управления (CFG) Выполнение на виртуальной машине Построение синтаксического дерева (AST) Компиляция графа в байт- код
  7. quantori.com 9 Микро-оптимизация кода на Python CPython оптимизации • Кэширование

    объектов int • Интернирование строк • Свертка констант (БЕЗ продвижения констант) • Удаление мертвого кода • Удаление константных проверок в if
  8. quantori.com 10 Микро-оптимизация кода на Python PyPy оптимизации • Увеличенное

    число кэшированных int • Кэширование методов • Расширенный байткод • JIT
  9. quantori.com 11 Микро-оптимизация кода на Python Просмотр bytecode Модуль dis

    поддерживает анализ CPython байткода и его дизассемблирование. Байткод является деталью реализации интерпретатора! dis.dis(function)
  10. quantori.com 12 Микро-оптимизация кода на Python Свертка констант def folding():

    x = 1 + 2 0 LOAD_CONST 1 (3) 2 STORE_FAST 0 (x) 4 LOAD_CONST 0 (None) 6 RETURN_VALUE
  11. quantori.com 13 Микро-оптимизация кода на Python Продвижение констант def propagation():

    x = 1 + 2 y = x + 2 0 LOAD_CONST 1 (3) 2 STORE_FAST 0 (x) 4 LOAD_FAST 0 (x) 6 LOAD_CONST 2 (2) 8 BINARY_ADD 10 STORE_FAST 1 (y) 12 LOAD_CONST 0 (None) 14 RETURN_VALUE
  12. quantori.com 14 Микро-оптимизация кода на Python Удаление константных проверок в

    if def const_if(data): if True: return data+1 0 LOAD_FAST 0 (data) 2 LOAD_CONST 1 (1) 4 BINARY_ADD 6 RETURN_VALUE
  13. quantori.com 18 Микро-оптимизация кода на Python Что можно оптимизировать вручную?

    • Продвижение констант • Доступ к атрибуту • Встраивание вызовов функций • Распаковка вместо взятия индекса • Раскрутка цикла
  14. quantori.com 20 Микро-оптимизация кода на Python Доступ к атрибуту class

    B: def attr(self): pass b = B() def original(): for _ in range(1000): b.attr() def ideal(): attr = b.attr for _ in range(1000): attr()
  15. quantori.com 21 Микро-оптимизация кода на Python Распаковка и обращение по

    индексу data = [(i,i**2) for i in range(100)] def original(): res = 0 for item in data: res += item[0] + item[1] def ideal(): res = 0 for i, square in data: res += i + square
  16. quantori.com 22 Микро-оптимизация кода на Python Встраивание функций def inline(arg):

    return arg + 1 def original(): data = 1 return inline(1) def ideal(): data = 1 return data + 1
  17. quantori.com 24 Микро-оптимизация кода на Python Работа с AST для

    оптимизации функции 1 5 3 2 4 Получаем исходный код Модицифируем AST Выполняем код создания функции Парсим и получаем AST Компилируем в байткод
  18. quantori.com 26 Микро-оптимизация кода на Python Реализуем продвижение констант. Получаем

    AST import inspect import ast source = inspect.getsource(foo) tree = ast.parse(source) print(ast.dump(tree, indent=4))
  19. quantori.com 27 Микро-оптимизация кода на Python Реализуем продвижение констант. Полученный

    AST A = 1 def foo(a): return A + 2 Module( body=[FunctionDef( name='foo', args=arguments(args=[arg(arg='a')], ...), body=[Return( value=BinOp( left=Name(id='A', ctx=Load()), op=Add(), right=Constant(value=2)))], decorator_list=[]) ], type_ignores=[])
  20. quantori.com 28 Микро-оптимизация кода на Python Реализуем продвижение констант. NodeTransformer

    • Для обхода дерева используется паттерн “Посетитель” • Для модификации наследуемся от ast.NodeTransformer • Методы посещения элементов имеют имена вида visit_имякласса (visit_Return, visit_If) • Для обхода поддерева не надо забывать вывать generic_visit
  21. quantori.com 29 Микро-оптимизация кода на Python Реализуем продвижение констант. Реализуем

    трансформер class Optimizer(ast.NodeTransformer): def __init__(self, constants) -> None: self.constants = constants def visit_Return(self, node: ast.Return) -> Any: node.value = eval_const(node.value, self.constants) return self.generic_visit(node) visit_Assign = visit_Return def visit_If(self, node: ast.If) -> Any: node.test = eval_const(node.test, self.constants) return self.generic_visit(node)
  22. quantori.com 30 Микро-оптимизация кода на Python Реализуем продвижение констант. Реализуем

    вычисление def eval_const(expr: ast.expr, constants: dict) -> ast.expr: if not expr: return expr try: expression = ast.Expression(body=expr) code = compile(expression, filename='', mode='eval') res = eval(code, constants.copy()) return ast.Constant(value=res, lineno=expr.lineno, col_offset=expr.col_offset) except Exception: return expr
  23. quantori.com 31 Микро-оптимизация кода на Python Реализуем продвижение констант. Применяем

    оптимизатор constants = { name: value for name, value in foo.__globals__.items() if name.isupper() } new_tree = Optimizer(constants=constants).visit(tree) bytecode = compile(new_tree, filename=inspect.getfile(foo), mode='exec') namespace = {} exec(bytecode, namespace) foo = namespace['foo']
  24. quantori.com 32 Микро-оптимизация кода на Python Реализуем продвижение констант. Оптимизированный

    AST def foo(a): return 3 Module( body=[FunctionDef( name='foo', args=arguments(args=[arg(arg='a')], ...), body=[ Return(value=Constant(value=3)) ], decorator_list=[]) ], type_ignores=[], )
  25. quantori.com 33 Микро-оптимизация кода на Python Реализуем продвижение констант. Более

    сложный пример A = 1 def SQUARE(x): return x ** 2 def BAR(): return A + SQUARE(2) def foo(a): x = 1 + 2 + 3 if 1 + 2 > 0: return BAR() + 2 return BAR() + 1 def foo(a): x = 6 if True: return 7 return 6 0 LOAD_CONST 1 (6) 2 STORE_FAST 1 (x) 4 LOAD_CONST 2 (7) 6 RETURN_VALUE
  26. quantori.com 35 Микро-оптимизация кода на Python Сложности работы с AST

    • Несоответствие трейсов коду • Сильные ограничения на оптимизируемый код • Эффективные алгоритмы сложны в реализации • Эффект в реальности небольшой
  27. quantori.com 36 Микро-оптимизация кода на Python Оптимизаторы из интернета •

    vstinner/fatoptimizer • martmists-gh/BytecodeOptimizer • kevinconway/pycc • lihaoyi/macropy Более не поддерживаются!