fa
Feedback
P(hD)ython

P(hD)ython

رفتن به کانال در Telegram

О Python, PhD, распределённых системах и не только Автор - Михаил Масягин (@masyagin1998): - Python Lead в NDA HFT; - преподаватель в Бауманке; - эксперт по СУБД System Design World; - любитель PhD и авторегрессии.

نمایش بیشتر
کشور مشخص نشده استدسته بندی مشخص نشده است
214
مشترکین
اطلاعاتی وجود ندارد24 ساعت
اطلاعاتی وجود ندارد7 روز
اطلاعاتی وجود ندارد30 روز
آرشیو پست ها
Приветствую Вас на канале P(hD)ython 👋 Меня зовут Михаил Масягин. Я тимлид, разработчик, аспирант и преподаватель МГТУ им. Н
Приветствую Вас на канале P(hD)ython 👋 Меня зовут Михаил Масягин. Я тимлид, разработчик, аспирант и преподаватель МГТУ им. Н.Э. Баумана. Сейчас я руковожу backend- и frontend-разработкой в HFT-компании. До этого были Lawful Interception и Bare Metal-проекты, работа с AWS и даже погружение в ML и NLP. С опытом я понял, что самые ценные знания обычно не попадают в учебники. Они появляются при решении реальных задач - через ошибки, багфиксы и дебаг, и, увы, часто теряются. Именно поэтому появился этот канал. Здесь я буду делиться тем, что считаю реально полезным: ⚡️ Python и современные практики разработки ⚡️ оптимизация кода и performance engineering ⚡️ C, Linux и немного Bare Metal ⚡️ распределённые системы и архитектура ⚡️ алгоритмы и структуры данных ⚡️ HFT и инженерные решения из индустрии ⚡️ опыт из преподавания, аспирантуры и написания диссертации Если Вам интересно не просто писать код, а понимать, почему он работает именно так, - добро пожаловать 🤝 С уважением, Михаил Масягин

test

«Финишная прямая 🎓» Научный руководитель наконец одобрил текст диссертации, и сегодня я отнёс «кирпич» в 2-х экземплярах на
«Финишная прямая 🎓» Научный руководитель наконец одобрил текст диссертации, и сегодня я отнёс «кирпич» в 2-х экземплярах на финальную проверку на кафедру 😎! Думаю, есть шанс, что первая предзащита будет в текущем учебном году (в июне). С уважением, Михаил Масягин P.S. А ещё со следующего учебного года ассистент становится старшим преподавателем 😎

«Data Lake: от перестановки мест слагаемых сумма... меняется? 👷» Недавно проводил лекцию по DWH на курсе System Design от ne
«Data Lake: от перестановки мест слагаемых сумма... меняется? 👷» Недавно проводил лекцию по DWH на курсе System Design от nevzorov.courses. На лекции разбирали довольно частый практический кейс: - есть ряд поддерживаемых источников данных (Sources); - есть множество клиентов (Customers); - для каждого клиента необходимо сохранять и обрабатывать данные из его источников (Customer Sources); - вопрос: как лучше спроектировать Data Lake под эту задачу? Вариант 1: customers/<customer_name>/source=<source_name> Вариант 2: sources/<source_name>/customer=<customer_name> Интуитивно рука тянется к 1 варианту... Однако для Data Lake и дальнейшей DWH-инфраструктуры часто лучше именно 2 вариант:
raw/sources/<source_name>/customer=<customer_name>/...
cleaned/sources/<source_name>/customer=<customer_name>/...
...
Например:
...
raw/sources/google_play/customer=rammstein/dt=2026-05-24/*.parquet
raw/sources/google_play/customer=sabaton/dt=2026-05-24/*.parquet
raw/sources/google_play/customer=megadeth/dt=2026-05-24/*.parquet
...
raw/sources/trustpilot/customer=rammstein/dt=2026-05-24/*.parquet
raw/sources/trustpilot/customer=led_zeppelin/dt=2026-05-24/*.parquet
raw/sources/trustpilot/customer=lordi/dt=2026-05-24/*.parquet
...
Почему? 1. Source естественным образом превращается в таблицу. Для AWS Athena, Apache Trino или Apache Spark - google-play, trustpilot и т.д. - это отдельные логические таблицы, разложенные по Parquet-файлам и партициям в виде Customer'ов:
SELECT
    *
FROM
    "raw"."google_play"
WHERE
    ("customer" = 'rammstein') AND ("dt" >= DATE '2026-05-01');
У google-play даже в сыром виде (и уж тем более в очищенном) есть какая-то своя схема данных, ключи, timestamp'ы, правила дедупликации, SLA, логика инкрементальной загрузки и т.д. У trustpilot и любого другого Source'а - свои. Если же сделать наоборот:
...
raw/customers/rammstein/source=google_play/dt=2026-05-24/*.parquet
raw/customers/rammstein/source=trustpilot/dt=2026-05-24/*.parquet
...
raw/customers/sabaton/source=google_play/dt=2026-05-24/*.parquet
...
то один логический источник google-play размазывается по разным корням. А дальше начинается адъ 👹: - отдельные таблицы на каждый Source каждого Customer'а; - UNION ALL запросы и VIEW-шки; - бардак с Data Governance. В общем, Data Lake, а следом за ним и DWH медленно, но неотвратимо превращаются в DataSwamp 😄 2. Data Mesh проще делать именно по Source'ам. Естественная единица владения - это не «папка клиента» (Customer), а доменный источник (Source). У каждого такого Source'а есть отдельная команда-владелец, контракты, документация, SLA, data quality checks и правила эволюции схемы. Команда, отвечающая за google_play, должна владеть одной папкой sources/google_play/customer=<customer_name>/*, а не тысячами подпапок customers/*/source=google_play/*. - Добавили нового клиента? Добавили новую партицию. - Поменяли контракт источника? Обновили один data product. - Поймали баг в ingestion? Чиним одний единственный ETL-pipeline. 3. Pipeline'ы обычно тоже мыслят именно Source'ами:
...
google_play
trustpilot
...
А не:
...
ingest_rammstein_everything
ingest_sabaton_everything
ingest_led_zeppelin_everything
...
Иначе очень быстро появляются «особые клиенты»: - у этого legacy CSV; - у этого timezone в строке; - у этого timestamp иногда null; - у этого producer шлёт дубликаты; - у этого «ну вы там руками поправьте, пожалуйста». Поздравляю, у вас не DWH, а зоопарк с Airflow DAG-ами 🦓 4. Наконец, Source-First Layout упрощает сложную аналитику:
SELECT
    "customer", count(*)
FROM
    "raw"."google_play"
WHERE
    "dt" = DATE '2026-05-24'
GROUP BY
    "customer";
Можно с лёгкостью строить Usage-Based Billing по конкретным Source'ам, позволять даже менеджменту без труда копаться в данных и т.д. Таким образом, проектируя DWH-систему лучше думать не о том, какие у вас будут клиенты, а о том, какие источники данных вы будете для них поддерживать. С уважением, Михаил Масягин

«Cursor на миллиард 🤑» В нашей команде мы активно используем множество ИИ-инструментов, в том числе Cursor. Сидим на Teams P
«Cursor на миллиард 🤑» В нашей команде мы активно используем множество ИИ-инструментов, в том числе Cursor. Сидим на Teams Plan. И сегодня я нашёл в этом «плане» неприятный сюрприз. В Teams Plan команда представляет собой одного админа (Admin) и множество обычных пользователей (Member). При этом имеется возможность ограничить расход средств, выставив максимально допустимую месячную сумму, которую команда тратит на токены: превысил лимит - жди следующего месяца. Но оказалось, что по умолчанию функция выставления лимитов доступна не только админу, но и любому члену команды! Да-да, не админу, не владельцу карты, а обычному Member-у! Имхо, это крайне неочевидное и небезопасное поведение, о котором документация упоминает лишь всколзь. Заходишь в Settings → Spend Limit → Team Spend Limit, ставишь лимит в миллиард долларов 💵 и уходишь на ночь, запустив 1000 и 1 агента 😎. Auto-моделька Cursor уверенно говорит, что это не баг, а фича, дабы «упростить онбординг команды» 😁 Чтобы запретить это веселье, нужно отдельно включить тумблер: Settings → Usage-Based Pricing Settings → Admin-only modifications. После этого вкладка Spend Limit исчезает у обычных пользователей. Интересная, конечно, помощь в онбординге команды... С уважением, Михаил Масягин P.S. Может, имелся в виду онбординг команды топ-менеджеров Cursor на очередную яхту 🧐?

«Python 3.15 beta: что нового 🐍» 7 мая зафризили фичи Python 3.15, и сейчас, в длинные выходные, самое время обсудить ключев
«Python 3.15 beta: что нового 🐍» 7 мая зафризили фичи Python 3.15, и сейчас, в длинные выходные, самое время обсудить ключевые изменения. Сразу уточню, что полный стабильный релиз будет 1 октября, поэтому пока что катаемся на test- и debug- ENV-ах 🤓. 1. Lazy imports (PEP 810) 🥱 В язык завезли новое ключевое слово lazy. Ленивый модуль загружается только при непосредственном обращении к его коду, что ускоряет старт Python-процесса:
lazy import numpy as np
lazy from pandas import DataFrame

df = DataFrame()  # только здесь pandas реально загрузится
Можно включить глобально через флаг -X lazy_imports=all или переменную PYTHON_LAZY_IMPORTS. 2. Распаковка в comprehensions (PEP 798) 📦 Самое долгожданное расширение синтаксиса за годы. Теперь * и ** работают внутри list/set/dict-comprehensions и генераторов:
lists = [[1, 2], [3, 4], [5]]
flat  = [*L for L in lists]                # [1, 2, 3, 4, 5]
merged = {**d for d in [{'a': 1}, {'b': 2}]}  # {'a': 1, 'b': 2}
То, что раньше писалось через itertools.chain.from_iterable или вложенные циклы, теперь - одна строка. Работает и в async for. Наконец вопрос на собесах «как разжать список списков» получил однозначный и окончательный ответ. 3. frozendict как builtin (PEP 814) 😎 «Замороженный» словарь - теперь встроенный тип. Можно класть в set, использовать ключом другого dict, да ещё и хэш не зависит от порядка вставки!
config = frozendict(host="localhost", port=5432)
cache  = {config: "primary"}
hash(frozendict(a=1, b=2)) == hash(frozendict(b=2, a=1))  # True
Также его подружили с copy, json, pickle, pprint. 4. sentinel builtin (реализация PEP 661) 🛡 Все мы писали этот хак: _MISSING = object(), чтобы отличать «не передал» от «передал None». Теперь это часть языка:
MISSING = sentinel("MISSING")

def get(d, key, default=MISSING):
    if default is MISSING:
        raise KeyError(key)
    return d.get(key, default)
Мелочь, а приятно. 5. Tachyon - сэмплирующий профайлер (PEP 799) 🔎 Появился пакет profiling с двумя бэкендами: profiling.tracing (бывший cProfile) и profiling.sampling - статистический профайлер с почти нулевым оверхедом. Самое крутое - сэмплирующий профайлер умеет подключаться к уже работающему процессу по его `PID`у:
python -m profiling.sampling --pid 12345 --format flamegraph -o out.svg
Кто хоть раз профилировал прод - понимает цену вопроса. 6. Очередное ускорение 🚀 Ускорили JIT (да, в CPython есть JIT, хоть и по умолчанию он недоступен!) на 8-9% на x86-64 Linux и на 12-13% на AArch64 macOS. Таким образом, 3.15 - это пусть и не «революционный», но важный релиз, значительно повышающий качество жизни разработчиков. Стандартная библиотека продолжает вбирать в себя то, что годами жило в формате рецептов на Stack Overflow. Это ли не говорит о зрелости языка? С уважением, Михаил Масягин

sticker.webp0.19 KB

«Мама, я в телевизоре 😎» Ну, может и не в телевизоре, но с первым опытом студийной записи меня 😅 С уважением, Михаил Масяги
+1
«Мама, я в телевизоре 😎» Ну, может и не в телевизоре, но с первым опытом студийной записи меня 😅 С уважением, Михаил Масягин

«Айтишники 💻 и металлурги 🛠» Последние пару месяцев активно провожу собесы Python-разработчиков: отвечаю за алгоритмическую
«Айтишники 💻 и металлурги 🛠» Последние пару месяцев активно провожу собесы Python-разработчиков: отвечаю за алгоритмическую секцию, где кандидатам предлагается решить несколько задач уровня LeetCode Easy/Medium и пообщаться о внутрянке Python. К сожалению, списывание и использование GPT на интервью лишь набирает обороты. Обычно это заметно довольно быстро: - либо человек не может объяснить «своё же» решение; - либо сыпется на каверзных вопросах про асимптотику, дополнительные ограничения и прочие нюансы. Недавно узнал, что в бигтехах 🏙 во время интервью кандидату могут задать пару случайных дурацких вопросов: - если человек честно говорит, что не знает - всё ок ✅; - а вот если отвечает, то, как модно сегодня говорить, это редфлаг ❌. И буквально час назад у меня случилась идеальная иллюстрация этого подхода. Кандидат ⭐️: - шикарный опыт; - почти 1 в 1 попадает в наш стэк; - решает задачи раза в полтора быстрее всех прошлых кандидатов; - знает абсолютно всё об asyncio; - strong hire! Но в какой-то момент в голове рождается мысль: а чем я хуже интервьюеров из бигтеха 😎? И звучит вопрос: - А расскажи мне, пожалуйста, про эвтектику в СУБД. (эвтектику, если что, мне подсказал GPT - как что-то максимально умное, солидное и при этом абсолютно не к месту) Кандидат без запинки отвечает, что проходил это ещё в вузе, и выдаёт какой-то поток несвязного бреда. Чувствую, что на подходе материал для поста (всё ради вас, подписчики ❤️), и решаю дожать: - Супер. А откуда это вообще пошло? Что такое эвтектика в исходном смысле? И тут человек снова без малейшей паузы выдаёт: - Эвтектика - это смесь двух или более веществ, которая плавится или затвердевает при фиксированной, самой низкой температуре для данной системы, действуя как чистое вещество. За пару минут собеседование Python-разработчика превратилось в устный экзамен по металлургии! Похоже, дурацкие вопросы работают! Иногда даже слишком хорошо 🤓 P.S. Эвтектика - это вполне реальный термин из металлургии и неорганической химии. P.P.S. До сих пор не исключаю, что у человека первое образование было металлургическое 👀 С уважением, Михаил Масягин

«Мам, сфоткай типо я кант-трейдор 🥴» С уважением, Михаил Масягин
«Мам, сфоткай типо я кант-трейдор 🥴» С уважением, Михаил Масягин

«Отель для настоящих HFT-разрабов и квантов 😎!» С уважением, Михаил Масягин P.S. Кто угадает страну... тот молодец
«Отель для настоящих HFT-разрабов и квантов 😎!» С уважением, Михаил Масягин P.S. Кто угадает страну... тот молодец

«CQRS: нормально делай - нормально будет!» Разбавим Python-посты архитектурой! На днях со студентами System Design World обсу
«CQRS: нормально делай - нормально будет!» Разбавим Python-посты архитектурой! На днях со студентами System Design World обсуждали паттерны, и закономерно всплыл CQRS. Его просто обожают на System Design Interview, и... регулярно путают с CQS! Micro vs Macro CQS (Command-Query Separation) - это принцип создания классов и API. - Command-методы меняют состояние и либо НЕ отдают данные (void), либо возвращают служебные значения (id, ok, error и т.д.); - Query-методы возвращают данные и никогда НЕ меняют состояние. На простых классах от CQS мало пользы, зато при написании фабрик, репозиториев и прочих паттернов он реально выручает: - меньше неявного поведения; - проще кэшировать и оптимизировать; - проще поддерживать и дебажить код. Пример:
from dataclasses import dataclass

@dataclass(frozen=True)
class Car:
    num: str

class Base:
    def __init__(self):
        self._cs = {}

class FactoryBad(Base):
    # get with unexpected side effect
    def get(self, num: str) -> Car:
        if num not in self._cs:
            self._cs[num] = Car(num=num)
        return self._cs[num]

class FactoryCQS(Base):
    def find(self, num: str) -> Car | None:
        return self._cs.get(num)

    def get(self, num: str) -> Car:
        return self._cs[num]

    def register(self, num: str) -> None:
        if num in self._cs:
            raise ValueError(f"Car with number '{num}' already exists!")
        self._cs[num] = Car(num=num)
CQRS (Command Query Responsibility Segregation) - это архитектурный паттерн: - разные пути и модели данных для записи и чтения - write-side и read-side; - часто разные Handler'ы, контракты и схемы - но всё же это детали реализации. По сути CQRS - это CQS «на стероидах». CQRS - Кафка, Стриминг, 2 Сурса Частая ошибка - воспринимать CQRS как обязательную связку из условных read- и write-СУБД, очереди и Eventual Consistency. Действительно, так часто бывает, но в первую очередь CQRS - про разделение путей и моделей данных, а не про инфраструктуру. Начать внедрение CQRS можно и с 1 СУБД: - write - нормализованные таблицы под базовые сущности; - read - денормализованные таблицы/view под чтение; - если обновлять read-проекции синхронно с write-проекциями, можно получить и Strong Consistency. Зачем всё это CRUD-сервис «на всё» (create, update, get, find, ...) быстро «пухнет»: - чтение и запись смешиваются, API становится неочевидным; - хотим масштабировать чтение, но read-only инстансы вынужденно тащат write-зависимости и флаги/роутинг; - репозиторий превращается в комбайн с бесконечными зависимостями; - страдает производительность; - сложнее растить команду. CQRS предлагает решение этой проблемы: - изолированные Handler'ы для команд и запросов (часто реально «по 1 файлу на операцию»); - лишь нужные зависимости в каждом Handler'е - ускоряет разработку и реально отделяет write-side от read-side. CQRS - не серебряная пуля: - если проект компактный и несложный - лучше CRUD + нормальный репозиторий; - CQRS добавляет бойлерплейт. Даже если код «генерится Claude'ом», растет объём и контекст. Идемпотентность команд - must have Команды могут повторяться из-за retry, timeout и at-least-once доставки. Handler должен быть идемпотентным и не допускать создания дубликатов. Блеск CQRS CQRS раскрывается, когда система становится read-heavy/нуждается в разных формах данных. Тогда вы: - масштабируете read- и write-side независимо; - держите read-модели под конкретные задачи: поиск, отчёты и т.д. Отдельный плюс - несколько read-моделей одновременно: Postgres для запросов, Elastic для FTS и т.д. При этом они могут строить свои проекции из единого потока событий (event bus, outbox, CDC и т.д.). Отсюда и дружба с Event Sourcing: при хранении изменений как Event Log, проекции можно пересобирать с нуля. Идеи CQS и CQRS во многом звучат как «нормально делай - нормально будет», они очень интуитивны. Тем не менее выработка общих терминологии и понимания - это всегда большой плюс. В следующий раз разберём Event Sourcing! С уважением, Михаил Масягин P.S. Рекомендую к просмотру выступление Андрея Цветциха.

«Сказано - сделано 😎🚀!» @ashm_tech получил свой приз 📕! С уважением, Михаил Масягин
«Сказано - сделано 😎🚀!» @ashm_tech получил свой приз 📕! С уважением, Михаил Масягин

🎉 Розыгрыш завершен! 🏆 Победители: 1. @ashm_tech 🔍 Проверить результаты

«Итоги 2025: Python 3.14 🐍 и немного моей жизни 🎄» Ну что, под Новый Год самое время подвести итоги. 7 октября 2025 вышла ф
«Итоги 2025: Python 3.14 🐍 и немного моей жизни 🎄» Ну что, под Новый Год самое время подвести итоги. 7 октября 2025 вышла финальная версия Python 3.14.0, меняющая как внутреннее устройство языка, так и добавляющая в него новые полезные функции. Число и значимость изменений сравнимы с 3.4.0, подарившей нам asyncio в 2014 году! 1. Kill GIL! Как известно, у змей - раздвоенный язык, и Python теперь - не исключение. Начиная с 3.14.0 существуют 2 сборки CPython - стандартная 3.14 и... 3.14t без GIL! Теперь, пусть и с рядом новых трудностей, мы можем достигать в Python истинного параллелизма потоков исполнения прямо как в C\C++, Java и т.д.! 2. concurrent.interpreters - до тех пор, пока 3.14t не станет стандартом запуск нескольких независимых интерпретаторов всё ещё актуален. Что же, теперь мы можем делать это в рамках одного процесса, минимизируя траты на IPC! 3. Отладка современного Python-кода стала ещё проще и приятнее - к целевому процессу можно подключиться на лету без необходимости его перезапуска! 4. Инкрементальный GC - осталось лишь 2 поколения объектов: молодое и старое, и на каждом цикле GC сканируется всё молодое и эвристически лишь часть старого. За счёт этого работа сборщика мусора стала предсказуемее и стабильнее. 5. Ряд оптимизаций байткода Python, включая Tail Calling. 6. t-strings - всё те же f-strings, но теперь завёрнутые в отдельный класс! 7. Аннотации больше не тормозят import'ы, так как вычисляются отложенно, а ещё их можно анализировать через annotationlib. 8. Все мы знаем, что zstd - это база, а теперь это знает и Python! zstandard теперь входит в стандартную поставку интерпретатора! 9. И много-много чего ещё! Все фичи подробно разберём в постах 2026 года! Ну и пару слов о моих итогах 😅: 1. Начал разбирать Кабанчика 🐷 на System Design World с @vova_dev! 2. Запустили с @vova_dev наш курс по System Design! Уже прошло 3 потока и на подходе 4! 3. + 1 новая статья ВАК/WoS и Conference Paper в IEEE - за последнее отдельное спасибо @Kvassir! 4. Выступил с лекциями по HFT на ICPC, FaangTalk (@volyx ❤️) и в Бауманке! 5. Пережил ремонт и даже несмотря на него побывал в новой стране - Вьетнаме! 6. Познакомился со столькими замечательными людьми: @vova_dev, @LooksOfTheMoon, @terapsyda - вы лучшие ❤️! 7. Начал активно собесить не только питонистов, но и растовчан на работе 💪! 8. Завёл канал, где вы сейчас и читаете этот пост! Нас уже 160+ 👫! Были и факапы, главный - диссер с конца 2025 года переехал на весну 2026 года 😢. К чему я это всё? Думаю, к тому, что надо работать, фигачить, выкладываться на 110%! Достичь всего - нереально, многого - очень сложно, но возможно 💪! С Новым Годом, друзья 🎄! Соблюдаем Work-Job Balance и идём седлать огненного коня 2026 года 🎠! С уважением, Михаил Масягин

«Розыгрыш 🎰, дедлайны ❌ и Рождество 🎄» Ну что, классика жанра: к дедлайну я, как всегда, не успел - поэтому розыгрыш будет
«Розыгрыш 🎰, дедлайны ❌ и Рождество 🎄» Ну что, классика жанра: к дедлайну я, как всегда, не успел - поэтому розыгрыш будет не к Новому году, а… к Рождеству 😅 В честь праздников разыгрываю среди подписчиков бумажную книгу Майкла Льюиса «Flash Boys: A Wall Street Revolt» - самое то, чтобы начать плавное погружение в HFT, попивая какао ☕️ у рождественской ёлки 🎄 Если вы в Москве - с радостью вручу лично и угощу кофе/пивом/какао 🍾 Как участвовать: - подписаться на канал (нас уже 150+ - спасибо вам ❤️); - нажать кнопку «Участвую!» под постом; - сделать репост в своё сообщество, добавив друзей по ссылке и т.д. - шанс на победу x2 💰; - ждать итогов 7 января 🎁. С уважением, Михаил Масягин

«Python для собесов - 2026 🚀» Продолжаем серию постов для Python-собесов! В прошлых постах мы научились ускорять Python (FFI
«Python для собесов - 2026 🚀» Продолжаем серию постов для Python-собесов! В прошлых постах мы научились ускорять Python (FFI & Python Compilers). Теперь поговорим о том, что именно нужно ускорять, потому что оптимизировать абсолютно всё - сложно, долго и дорого (да зачастую и не нужно) 😵. Базовый алгоритм выглядит следующим образом: 1. Замерили время выполнения кода. 2. Нашли узкое место. 3. Оптимизировали. 4. Проверили, что стало лучше. Если нет или недостаточно, то возвращаемся к пункту 1 😄. Начнём с простейших измерений. На собесах часто спрашивают как в коде замерить время выполнения отдельной функции. К сожалению, ответ time() встречается неприлично часто. Однако time() это так называемые «wall clock» 🕚 - они не монотонны (могут перепрыгивать из-за NTP, смены поясов, гибернации), а их точность и разрешение невысоки и зависят от настроек ОС. Вместо них лучше использовать монотонные счётчики: - perf_counter_ns() - точный и монотонный аналог time() (для общих случаяев); - process_time_ns() - только время на CPU (удобен для числодробилок). Пример кода:
from time import perf_counter_ns as pc_ns

def work(n):
    s = 0
    for i in range(n):
        s += i*i
    return s

if __name__ == "__main__":
    t = pc_ns()
    work(1000000)
    dt = pc_ns() - t
    print(f"{dt/1e6:.3f} ms")
и его запуск:
python3 pc_ns_demo.py 
23.767 ms
Один прогон не слишком показателен - виной тому «шум» планировщика, кэши, вызовы GC и т.д. diff счётчиков можно сохранять в массив в цикле, но лучше использовать готовый инструмент - timeit. Он позволяет запускать много раз функции или даже программы, задавая как число прогонов в замере (number), так и число самих замеров (repeat), тем самым получая устойчивое распределение времени. Пример кода:
import timeit
from pc_ns_demo import work

rs = timeit.repeat(lambda: work(1000000), number=10, repeat=10)

print("runs:")
for i, t in enumerate(rs, 1):
    print(f"  {i:02d}: {t/10*1e3:.3f} ms") 

bs = min(rs)
print(f"min: {bs/10*1e3:.3f} ms")
и его вызов:
python3 timeit_demo.py 
runs:
  01: 23.849 ms
  ...
  10: 23.650 ms
min: 23.650 ms
Ещё короче:
python3 -m timeit -n 10 -r 10 -u msec -s "from pc_ns_demo import work" "work(1000000)"
10 loops, best of 10: 23.5 msec per loop
Поговорим о профилировании - определении конкретных медленных участков кода с cProfile. Он позволяет подсчитать число вызовов каждой функции (ncalls), её целевое и суммарное время исполнения (без/с учётом вызовов функций внутри) (tottime, percall, cumtime, percall) с привязкой к определению (filename:...). Запуск профайлера для программы:
python3 -m cProfile -o prof.bin pc_ns_demo.py
24.467 ms

python -c "import pstats;p=pstats.Stats('prof.bin');p.sort_stats('tottime').print_stats(20)"
Fri Dec 19 06:00:17 2025    prof.bin

         7 function calls in 0.024 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.024    0.024    0.024    0.024 pc_ns_demo.py:3(work)
   ...
Файл prof.bin можно визуализировать пакетом snakeviz - скрин в комментах. И внутри python-кода:
import cProfile
import pstats
from io import StringIO
from pc_ns_demo import work

def kek(a, b, c):
    return work(a) + work(b) + work(c)

pr = cProfile.Profile()
pr.enable()
print(kek(1000000, 2000000, 3000000))
pr.disable()

s = StringIO()
pstats.Stats(pr, stream=s).sort_stats("cumtime").print_stats(5)
print(s.getvalue())
и запуск:
python3 cprofile_demo.py
11999993000001000000
         6 function calls in 0.146 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.146    0.146 /home/mikhail/cprofile_demo.py:6(kek)
        3    0.146    0.049    0.146    0.049 /home/mikhail/pc_ns_demo.py:3(work)
...
Существуют и другие инструменты вроде py-spy и scalene. Они позволяют периодически дампить callstack процесса, а scalene показывает ещё и аллокации 🤓. Вот мы и познакомились с джентльменским набором тайминга и профайлинга в Python! ⚙️🚀 С уважением, Масягин Михаил

«А почему бы и... да 😂😎!» https://youtube.com/live/F8A6Nq8c13U?feature=share https://t.me/faangtalk_news/329 И уже в эту среду в 9 вечера по Москве самая полная, подробная и хардокрная версия HFT-доклада! Да ещё и под соусом System Design! Да ещё и у самих t.me/faangtalk 😎! С уважением, Михаил Масягин

«Эффект попугая у публичных спикеров 🦜» Периодически посматриваю лекции и выступления известных учёных и программистов - на YouTube, в подкастах, на конференциях и т.д. С лёгким ужасом для себя обнаружил, что большинство профессиональных спикеров на разных площадках рассказывают одно и то же: те же истории, те же тезисы, иногда даже те же слайды. Но чем больше я на это смотрю, тем сильнее понимаю: этого не избежать. Со временем у тебя формируется стабильный набор экспертных тем, десяток любимых слайдов и пара-тройка шуток - они становятся таким же твоим неизменным атрибутом, как потрёпанный портфельчик у Жванецкого 😅 https://t.me/studsovet_iu/1319 Уже в следующую среду читаю расширенный доклад о современной HFT-инфраструктуре в Бауманке. Постепенно превращаюсь в профессионального спикера. Обещаю, что добавлю хотя бы один новый слайд и пару новых шуток, но это не точно 😄 P.S. Помню о рубрике «Python для собесов - 2026 🚀». На выходных выложу новый пост 🤝! С уважением, Михаил Масягин

«Горячий HFT-доклад 🔥» Выложил запись своего выступления про HFT-инфраструктуру с Moscow ICPC 2025 на YouTube! https://youtu.be/us53niWItTg?si=UwSvvEsaJMDFJ_RM Смотрите, комментируйте, поддерживайте лайками и репостами! В докладе про HFT, FPGA, Kernel-Bypass и много-много денег 🤑! P.S. Сразу после выступления очень сильно заболел, поэтому видео немного задержалось. Но лучше поздно, чем никогда 💪! С уважением, Михаил Масягин