company_banner

Профилирование и отладка Python, переходим к практике

    В прошлой статье мы определили понятия профилирования и оптимизации, познакомились с различными подходами к профилированию и видами инструментов. Немного коснулись истории профайлеров.

    Сегодня я предлагаю перейти к практике и покажу на примерах способы ручного профилирования (и даже «метод пристального взгляда». Будут так же рассмотрены инструменты для статистического профилирования.

    • Введение и теория — зачем вообще нужно профилирование, различные подходы, инструменты и отличия между ними
    • Ручное и статистическое профилирование — переходим к практике
    • Событийное профилирование — инструменты и их применение
    • Отладка — что делать, когда ничего не работает

    Тренируемся


    Какая может быть практика без примеров и тренировок? Я долго думал, какой же проект стоит взять в качестве учебного пособия, чтобы он был одновременно показательным и не очень сложным. На мой взгляд таковым является Project Euler — сборник математических и компьютерных головоломок, поскольку для решения предложенных задач приходится применять числодробительные алгоритмы и без оптимизации поиск ответа может продолжаться годами.

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

    В качестве примера для статьи возьмём задачу 3:
    Простые делители числа 13195 — это 5, 7, 13 и 29.
    Какой самый большой делитель числа 600851475143, являющийся простым числом?

    Пишем простое решение «в лоб»:
    """Project Euler problem 3 solve"""
    from __future__ import print_function
    import sys
     
     
    def is_prime(num):
        """Checks if num is prime number"""
        for i in range(2, num):
            if not num % i:
                return False
        return True
     
     
    def find_prime_factors(num):
        """Find prime factors of num"""
        result = []
        for i in range(2, num):
            if is_prime(i) and not num % i:
                result.append(i)
        return result
     
     
    if __name__ == '__main__':
        try:
            num = int(sys.argv[1])
        except (TypeError, ValueError, IndexError):
            sys.exit("Usage: euler_3.py number")
        if num < 1:
            sys.exit("Error: number must be greater than zero")
     
        prime_factors = find_prime_factors(num)
        if len(prime_factors) == 0:
            print("Can't find prime factors of %d" % num)
        else:
            print("Answer: %d" % prime_factors[-1])

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

    Для проверки запускаем программу c числом, делители которого нам известны:

    rudnyh@work:~/work/python-profiling (venv: python-profiling)
     python euler_3.py 13195
    Answer: 29

    Ответ 29, выданный программой правильный (его можно найти в условии задачи). Ура! Теперь запускаем программу с интересующим нас числом 600851475143:
    rudnyh@work:~/work/python-profiling (venv: python-profiling)
     python euler_3.py 600851475143

    И… ничего не происходит. Загрузка процессора 100%, выполнение программы не завершилось даже после нескольких часов работы. Начинаем разбираться. Вспоминаем, что оптимизировать можно только тот код, который работает корректно, но мы проверили работу программы на небольшом числе и ответ был правильный. Очевидно, что проблема в производительности и нам нужно приступать к оптимизации.

    Метод пристального взгляда


    В реальной жизни я бы профилировал программу с аргументом 13195 (когда время её работы адекватно). Но поскольку мы тут тренируемся и вообще, just for jun, воспользуемся «методом пристального взгляда».

    Открываем код и внимательно на него смотрим. Понимаем (если повезёт, то понимаем быстро), что для поиска делителей числа N нам нет смысла перебирать числа из интервала sqrt(N)+1…N-1, т.к. все делители из этого диапазона мы уже нашли при переборе чисел из интервала 2…sqrt(N). Слегка модифицируем код (см. строки 9 и 18):
    """Project Euler problem 3 solve"""
    from __future__ import print_function
    import math
    import sys
     
     
    def is_prime(num):
        """Checks if num is prime number"""
        for i in range(2, int(math.sqrt(num)) + 1):
            if not num % i:
                return False
        return True
     
     
    def find_prime_factors(num):
        """Find prime factors of num"""
        result = []
        for i in range(1, int(math.sqrt(num)) + 1):
            if is_prime(i) and not num % i:
                result.append(i)
        if is_prime(num):
            result.append(i)
        return result
     
     
    if __name__ == '__main__':
        try:
            num = int(sys.argv[1])
        except (TypeError, ValueError, IndexError):
            sys.exit("Usage: euler_3.py number")
        if num < 1:
            sys.exit("Error: number must be greater than zero")
     
        prime_factors = find_prime_factors(num)
        if len(prime_factors) == 0:
            print("Can't find prime factors of %d" % num)
        else:
            print("Answer: %d" % prime_factors[-1])

    Для проверки ещё раз запускаем программу с числом, делители которого нам известны:

    rudnyh@work:~/work/python-profiling (venv: python-profiling)
     python euler_3.py 13195
    Answer: 29

    Cубъективно программа отработала гораздо быстрее, значит снова запускаем её с интересующим нас числом 600851475143:
    rudnyh@work:~/work/python-profiling (venv: python-profiling)
     python euler_3.py 600851475143
    Answer: 6857

    Проверяем ответ на сайте, он оказывается верным, задача решена, мы чувствуем моральное удовлетворение.

    Программа выполнилась за приемлимое время (меньше минуты), ответ получен верный, смысла в дальнешей оптимизации в данном конкретном случае нет, т.к. поставленную задачу мы решили. Как мы помним, самое важное в оптимизации — уметь вовремя остановиться.

    Да, я в курсе, что ребята из «Project Euler» просят не выкладывать ответы и решения в общий доступ. Но ответ к задаче 3 гуглится (например, по условию «project euler problem 3 answer» на раз, поэтому я считаю, что ничего страшного в том, что я написал ответ нет.

    Ручное профилирование


    Один из самых распространённых способов быстро прикинуть «что к чему». В самом элементарном случае, если мы используем unix-утилиту «time» это выглядит так (до оптимизации):
    rudnyh@work:~/work/python-profiling (venv: python-profiling)
     time python euler_3.py 13195
    Answer: 29
    python euler_3.py 13195  3,83s user 0,03s system 99% cpu 3,877 total

    И так (после оптимизации):
    rudnyh@work:~/work/python-profiling (venv: python-profiling)
     time python euler_3.py 13195
    Answer: 29
    python euler_3.py 13195  0,03s user 0,02s system 90% cpu 0,061 total

    Ускорение почти в 65 раз (с ~3,87 секунд до ~61 милисекунд)!

    Так же ручное профилирование может выглядеть так:
    import time
     
    ...
     
    start = time.time()
    prime_factors = find_prime_factors(num)
    print("Time: %.03f s" % (time.time() - start))

    Результат:
    rudnyh@work:~/work/python-profiling (venv: python-profiling)
     python euler_3.py 600851475143
    Answer: 6857
    Time: 19.811 s

    Либо с использованием специального модуля «timeit», который предназначен для измерения быстродействия небольших программ. Пример применения:
    rudnyh@work:~/work/python-profiling (venv: python-profiling)
     python -m timeit -n 10 -s'import euler_3' 'euler_3.find_prime_factors(600851475143)'
    10 loops, best of 3: 21.3 sec per loop

    Когда же можно применять ручное профилирование? Во-первых, это отличный способ для проведения различного рода соревнований между разработчиками («Мой код теперь работает быстрее твоего, значит я более лучший программист!»), и это хорошо. Во-вторых, когда требуется «на глазок» определить быстродействие программы (20 секунд? долго!) или получить результаты достигнутых улучшений (ускорил код в 100 раз!).

    Но самое важное применение — это сбор статистики времени выполнения кода практически в реальном времени прямо на продакшене. Для этого измеренное время отправляем в любую систему сбора метрик и отрисовки графиков (я очень люблю использовать Graphite и StatsD в качестве аггрегатора для графита).

    Для этого можно воспользоваться простым менеджером контекстов:
    """Collect profiling statistic into graphite"""
    import socket
    import time
     
     
    CARBON_SERVER = '127.0.0.1'
    CARBON_PORT = 2003
     
     
    class Stats(object):
        """Context manager for send stats to graphite"""
     
        def __init__(self, name):
            self.name = name
     
        def __enter__(self):
            self.start = time.time()
            return self
     
        def __exit__(self, *args):
            duration = (time.time() - self.start) * 1000  # msec
            message = '%s %d %d\n' % (self.name, duration, time.time())
     
            sock = socket.socket()
            sock.connect((CARBON_SERVER, CARBON_PORT))
            sock.sendall(message)
            sock.close()

    Пример его использования:
    from python_profiling.context_managers import Stats
     
    ...
     
    with Stats('project.application.some_action'):
        do_some_action()

    Или простым декоратором:
    """Collect profiling statistic into graphite"""
    import socket
    import time
     
     
    CARBON_SERVER = '127.0.0.1'
    CARBON_PORT = 2003
     
     
    def stats(name):
        """Decorator for send stats to graphite"""
        def _timing(func):
            def _wrapper(*args, **kwargs):
                start = time.time()
                result = func(*args, **kwargs)
                duration = (time.time() - start) * 1000  # msec
                message = '%s %d %d\n' % (name, duration, time.time())
     
                sock = socket.socket()
                sock.connect((CARBON_SERVER, CARBON_PORT))
                sock.sendall(message)
                sock.close()
     
                return result
            return _wrapper
        return _timing

    Пример использования декоратора:
    from python_profiling.decorators import stats
     
    ...
     
    @stats('project.application.some_action')
    def do_some_action():
        """Doing some useful action"""

    На выходе получаем график времени выполнения интересующего нас участка кода, например, вот такой:

    по которому всегда видно, как себя чувствует код на живых серверах, и не пора ли его оптимизировать. Видно, как код себя чувствует после очередного релиза. Если проводился рефакторинг или оптимизация, график позволяет своевременно оценить результаты и понять, улучшилась или ухудшилась ситуация в целом.

    У метода есть и недостатки, и самый главный из них заключается в отсутствии зависимости от входных данных. Так, для функции определения простого числа «is_prime» время выполнения будет сильно зависеть от величины этого числа, и если эта функция в проекте вызывается очень часто график окажется совершенно бессмысленным. Важно чётко понимать где какой подход можно использовать и что мы имеем на выходе.

    Конечно, можно назвать метод профилирования через сбор метрик в графит «статистическим», ведь мы собираем статистику работы программы. Но я предлагаю придерживаться принятой нами в первой части терминологии и под «статистическим» профилированием понимать именно сбор информации (семплирование) через определённые промежутки времени.

    Django StatsD


    В случае использования графита и аггрегирующего сервера (StatsD) для него мы для одной метрики получаем сразу несколько графиков: минимальное и максимальное время выполнения кода, а так же медиану и количество записанных показаний (вызовов функции) в единицу времени, что очень удобно. Давайте посмотрим как просто подключить StatsD к Django.

    Ставим модуль:
     pip install django-statsd-mozilla

    Добавляем настройки в settings.py: приложение и middleware:
    INSTALLED_APPS += ('django_statsd',)
     
    MIDDLEWARE_CLASSES += (
           'django_statsd.middleware.GraphiteRequestTimingMiddleware',
           'django_statsd.middleware.GraphiteMiddleware',
    )
     
    # send DB timings
    STATSD_PATCHES = ['django_statsd.patches.db']

    И всё! На выходе получаем вот такие вот графики:


    Плюсы и минусы StatsD:
    + прост в установке и использовании
    + годен для продакшена (must-have, в общем-то)
    – мало информации (количество/время)
    – Нужен graphite и statsd (тоже must-have)

    Статистические профайлеры


    В отличие от событийных профайлеров, инструментов для статистического профилирования немного. Я расскажу о трёх:


    StatProf


    Пожалуй, самый известный статистический профайлер под питон — statprof. Ставим:
     pip install statprof

    Используем, например. так:
    import statprof
     
    ...
     
    statprof.start()
    try:
        do_some_action()
    finally:
        statprof.stop()
        statprof.display()

    Или как менеджер контекста (нет в версии 0.1.2 из pypi, есть только в версии из репозитория):
    import statprof
     
    ...
     
    with statprof.profile():
        do_some_action()

    Попробуем отпрофилировать наш код:
    rudnyh@work:~/work/python-profiling (venv: python-profiling)
     python euler_3.py 600851475143
      %   cumulative      self
     time    seconds   seconds  name
     44.42      8.63      8.63  euler_3.py:12:is_prime
     37.12      7.21      7.21  euler_3.py:11:is_prime
     16.90     19.40      3.28  euler_3.py:21:find_prime_factors
      0.95      0.18      0.18  euler_3.py:9:is_prime
      0.48      0.09      0.09  euler_3.py:13:is_prime
      0.06      0.01      0.01  euler_3.py:14:is_prime
      0.06      0.01      0.01  euler_3.py:20:find_prime_factors
      0.03      0.01      0.01  euler_3.py:23:find_prime_factors
      0.00     19.42      0.00  euler_3.py:37:<module>
    — Sample count: 3575
    Total time: 19.420000 seconds
    Answer: 6857

    Видим два «хотспота» в нашей программе. Первый: строки 12 и 11 функции «is_prime» (что логично), их выполнение занимает около 82% времени работы программы, и второй: строка 21 функции «find_prime_factors» (около 17% времени):
    if is_prime(i) and not num % i:

    Именно из этой строки вызывается самая «горячая» функция программы «is_prime». Просто поменяв местами операнды в условии мы значительно ускорим программу, т.к. операция получения остатка от деления (num % i) выполняется быстрее функции «is_prime», и в то же время достаточно часто остаток от деления одного числа на другое не равен нулю, и «not num % i» вернёт False. Таким образом, мы кардинально уменьшим количество вызовов функции «is_prime»:
    if not num % i and is_prime(i):

    Запускаем профилирование:
    rudnyh@work:~/work/python-profiling (venv: python-profiling)
     python euler_3.py 600851475143
      %   cumulative      self
     time    seconds   seconds  name
     87.50      0.22      0.22  euler_3.py:21:find_prime_factors
      5.00      0.01      0.01  euler_3.py:20:find_prime_factors
      5.00      0.01      0.01  euler_3.py:11:is_prime
      2.50      0.01      0.01  euler_3.py:23:find_prime_factors
      0.00      0.25      0.00  euler_3.py:37:<module>
    — Sample count: 40
    Total time: 0.250000 seconds
    Answer: 6857

    Самое горячее место в нашей программе теперь строка 21 функции «find_prime_factors», то есть операция получения остатка от деления («num % i»). Функция «is_prime» теперь вызывается гораздо реже и выполняется всего 5% времени работы программы. Время работы программы значительно уменьшилось и наибольший простой делитель числа 600851475143 теперь находится всего за 0,25 секунд (ускорили программу почти в 80 раз).

    Обратим внимание на то, как сильно упала точность работы профайлера: вместо 3575 семплов (в примере до оптимизации) было сделано всего 40 измерений и получена информация всего о пяти строках. Конечно, этого недостаточно. Собственно, в этом и заключается особенность статистических профайлеров: чем больше времени мы собираем данные, тем точнее анализ. Если бы мы, например, запустили программу десять или сто раз, мы бы получили гораздо более точные результаты.

    Здесь я должен сделать небольшое лирическое отступление.
    99% всех вспомогательных утилит, таких как профайлеры или инструменты для анализа покрытия кода тестами (code coverage), работают со строками в качестве минимальной единицы информации. Таким образом, если мы будем пытаться писать код как можно более компактно, например так:
    result = foo() if bar else baz()

    мы не сможем в профайлере увидеть, какая из функций вызывалась, а какая нет. И в отчёте coverage мы не увидим, покрыт ли тот или иной вариант развития событий тестами. Несмотря на более привычный и, казалось бы, удобный код, в некоторых случаях всё-таки лучше писать так:
    if bar:
        result = foo()
    else:
        result = baz()

    и тогда мы сразу увидим какие строки не исполняются, а какие — исполняются (и как часто). Однострочники и компактность — это, конечно, хорошо и прикольно, но иногда лучше потратить больше времени и получить код, который легче профилировать, тестировать и поддерживать.


    Достоинства и недостатки statprof:
    + минимальный оверхед
    + простое использование
    – реализация достаточно сырая и экспериментальная
    – для адекватного результата нужен длительный анализ
    – мало данных на выходе

    Django-live-profiler


    Отдельным пунктом стоит отметить django-live-profiler — профайлер приложений Django, использующий statprof. Для его работы необходимо сначала установить zeromq:
     brew install zmq

    Ставим сам модуль:
     pip install django-live-profiler

    И запускаем аггрегатор:
     aggregated --host 127.0.0.1 --port 5556

    Дальше добавляем профайлер в settings.py:
    # добавляем application
    INSTALLED_APPS += ('profiler',)
     
    # добавляем middleware
    MIDDLEWARE_CLASSES += (
        'profiler.middleware.ProfilerMiddleware',
        'profiler.middleware.StatProfMiddleware',
    )

    И в urls.py:
    url(r'^profiler/', include('profiler.urls'))

    Запускаем сервер:
     python manage.py runserver --noreload --nothreading

    Открываем в браузере профайлер: 127.0.0.1:8000/profiler/ и радуемся жизни, наблюдая за результатами профилирования живого проекта в реальном времени:


    А ещё django-live-profiler умеет показывать SQL-запросы:


    Достоинства и недостатки django-live-profiler:
    + небольшой оверхед
    + можно пускать в продакшн (очень осторожно)
    + профилирование SQL-запросов
    – сложная установка, зависимости
    – мало данных на выходе

    Plop


    Ещё один статистический профайлер называется plop (Python Low-Overhead Profiler). Автор сразу предупреждает, что реализация сырая и проект находится в стадии активной разработки. Установка тривиальна:
     pip install plop tornado

    Запускаем профилирование:
    rudnyh@work:~/work/python-profiling (venv: python-profiling)
     python -m plop.collector euler_3.py 600851475143
    Answer: 6857
    profile output saved to /tmp/plop.out
    overhead was 5.89810884916e-05 per sample (0.00589810884916%)

    Запускаем сервер для просмотра результатов:
     python -m plop.viewer --datadir=/tmp/

    Открываем в баузере страницу http://localhost:8888/ и любуемся результатами:

    Plop можно использовать для профилирования Django-приложений. Для этого нужно установить пакет django-plop:
     pip install django-plop

    Добавить в settings.py middleware и параметр, указывающий профайлеру, куда складывать результаты:
    MIDDLEWARE_CLASSES += (
        'django-plop.middleware.PlopMiddleware',
    )
     
    PLOP_DIR = os.path.join(PROJECT_ROOT, 'plop')

    В большом проекте граф будет выглядеть более внушительно:

    Картина достаточно психоделична, серьёзным инструментом для профилирование это назвать сложно, но тем не менее граф вызовов у нас есть, самые горячие участки кода видно, оверхед минимальный (по утверждениям авторов всего 2%), и в некоторых случаях этого инструмента оказывается достаточно для обнаружения участков кода, нуждающихся в профилировании. Сервис Dropbox использует plop прямо в продакшене, а это о многом говорит.

    Достоинства и недостатки plop:
    + минимальный оверхед
    + можно пускать в продакшн
    – сложная установка, зависимости
    – очень мало данных на выходе

    New Relic


    Говоря о статистических профайлерах нельзя не сказать о сервисе New Relic, который предназначен не только для профилирования, но так же для мониторинга серверов и веб-приложений (а так же мобильных версий). Желающие могут посмотреть всю информацию на сайте компании, а так же попробовать сервис совершенно бесплатно. Я же не буду рассказывать о нём потому, что лично не пробовал работать с New Relic, а я привык говорить только о тех вещах, которые пробовал сам. Посмотреть скриншоты можно на странице, посвящённой профайлеру.

    Достоинства и недостатки:
    + предназначен для продакшена
    + много разного функционала (не только профилирование)
    – платный (есть бесплатная версия)
    – данные отправляются на чужие серверы

    В следующей статье мы перейдём к изучению событийных профайлеров — основными инструментами при профилировании Python. Оставайтесь на связи!
    Метки:
    Mail.Ru Group 1 008,72
    Строим Интернет
    Поделиться публикацией
    Похожие публикации
    Комментарии 25
    • НЛО прилетело и опубликовало эту надпись здесь
      • +3
        Если честно, не в курсе, потому что не использую IDE в работе.
        Гугл говорит, что вот это: pydev.org/ то, что нужно.
        • НЛО прилетело и опубликовало эту надпись здесь
          • +4
            Вопреки сложившимся мифам, нас в mail.ru не насилуют и не заставляют пользоваться продуктами компании в обязательном порядке. А ещё мы не поём гимны стоя по утрам и в целом — самые обычные люди.

            У Google лучше результаты запросов на тему программирования по англоязычным ресурсам — это факт. Лучше, чем Поиск Mail.ru и лучше, чем Яндекс. У Поиска Mail.ru другие преимущества. Это моё субъективное мнение, если неправ — прошу поправить.

            Или это троллинг такой толстый был а я типа такой наивный повёлся?
            • НЛО прилетело и опубликовало эту надпись здесь
      • +2
        Пользуюсь newrelic уже пару лет и очень доволен. В айфоне стоит приложение newrelic и я и оттуда могу наблюдать за жизнью проекта и если что реагировать.
        еще есть django-newrelic-extensions и newrelic-plugin-agent
        • 0
          Обязательно попробую его в одном из своих проектов. Спасибо!
        • +3
          Давно использую простой middleware, который позволяет прогнать любой View через профайлер и вывести результат прямо в браузер.
          gist.github.com/ikatson/7426817

          Естественно, без статистики, но для разработки (да и, честно говорят, для продакшена тоже) очень удобно :)

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

          Обычно бесплатной версии NewRelic + этого middleware + django-debug-toolbar хватает чтобы локализовать и нейтрализовать узкие места.
          • +2
            Спасибо, хороший вариант.
            В сочетании с функцией «Copy as cURL» в хроме:

            можно прямо из консоли вызывать любые запросы сколько угодно раз и сразу видеть результат.

            Кстати, зачем тег «pre», ведь можно указать заголовок «Content-Type: text/plain»?
            • +1
              Пытался вспомнить, зачем так сделал, проверил и вспомнил:

              когда ставишь text/plain, строки, которые длиннее экрана, переносятся в браузере на следующую строку, вместо включения горизонтального скролла, и вывод, скажем, «print_callees=1» превращается в малочитабельную кашу. Естественно, только при просмотре в браузере (может быть даже только в Chrome).

              Может быть это как-то можно обойти, но когда я писал этот скрипт несколько лет назад, это было не важно.
              UPD: Быстрое гугление говорит, что по-простому это не обойти.

              Но, в любом случае, спасибо, как минимум, комментария в коде этот хак заслужил.
              • 0
                Понял, спасибо за ответ!
          • +5
            Метод «пристального взгляда» в первой программе у вас неправильно сработал. Вы изменили сам смысл программы, и в итоге она работает неправильно, например, на числе 246 = 2*3*41 наивный вариант выдаёт 41, а «оптимизированный» — 3.
            • +3
              Ох ох ох, ваша правда. В реальной жизни, конечно, нужно всегда запускать тесты до и после оптимизации, а тут оказия вышла.
              • +4
                Пусть в статье пока останется неправильный вариант, чтобы все видели, какой я неудачник.

                Правильный вариант
                """Project Euler problem 3 solve"""
                from __future__ import print_function
                import math
                import sys
                
                
                def is_prime(num):
                    """Checks if num is prime number"""
                    for i in range(2, int(math.sqrt(num)) + 1):
                        if not num % i:
                            return False
                    return True
                
                
                def find_prime_factors(num):
                    """Find prime factors of num"""
                    result = []
                    for i in range(1, int(math.sqrt(num)) + 1):
                        if is_prime(i) and not num % i:
                            result.append(i)
                        if is_prime(num / i) and not num % i:
                            result.append(num / i)
                    if is_prime(num):
                        result.append(i)
                    return result
                
                
                if __name__ == '__main__':
                    try:
                        num = int(sys.argv[1])
                    except (TypeError, ValueError, IndexError):
                        sys.exit("Usage: euler_3.py number")
                    if num < 1:
                        sys.exit("Error: number must be greater than zero")
                
                    prime_factors = sorted(find_prime_factors(num))
                    if len(prime_factors) == 0:
                        print("Can't find prime factors of %d" % num)
                    else:
                        print("Answer: %d" % prime_factors[-1])
            • +1
              Да, я в курсе, что ребята из «Project Euler» просят не выкладывать ответы и решения в общий доступ. Но ответ к задаче 3 гуглится (например, по условию «project euler problem 3 answer» на раз, поэтому я считаю, что ничего страшного в том, что я написал ответ нет.
              Но всё же хотелось бы попросить вас скрыть правильные ответы за спойлером. Иначе решать самому становится менее интересно.
              • 0
                Разумно, учту.
                • 0
                  Эту задачу можно решить множеством способов на разных языках. Попробуйте придумать 10 решений…
                • +2
                  Еще IPython в профилировании помогает: там есть встроенные %prun, %%prun, %timeit и %%timeit, которые позволяют запускать cProfile и timeit над выражениями/ячейками. Если line_profiler и memory_profiler установить, то еще %lprun и %mprun добавляются. Очень удобно использовать с ipython notebook.

                  Из статистических профайлеров еще есть yappi, использовал его несколько лет назад; сейчас глянул в репозиторий — меньше суток назад последний коммит)
                  • 0
                    О, iPython — вообще отдельная история. Замечательнейший инструмент для отладки (и профилирования). Согласен, следует добавить в статью про "%prun, %%prun, %timeit и %%timeit" — сделаю!

                    Yappi не видел, обязательно ознакомлюсь и добавлю в статью =)
                  • +1
                    and not num % i:

                    Лучше, все же, писать 'and num % i == 0:'

                    Потому что иначе, поскольку интересный для нас результат — равенство выражения нулю, непроизвольно ожидается, что num % i будет True в том случае, когда он равен нулю, и not ошибочно кажется излишним.
                    • 0
                      Согласен, немного спорный участок кода.
                    • +1
                      Так как в итоге обстоит дело с профайлингом SQL запросов?

                      Дебаг тулбар мощный, но с ajax запросами не работает. Один мой товарищ написал специальную тулзу для дебаг-тулбара, чтобы через торнадо их перехватывать и выводить на отдельной странице, но это совсем жестко) к тому же дебаг тулбар использует свой jQuery что очень плохо.

                      Из альтернатив только Django-live-profiler? Он решает проблему ajax запросов?
                      • 0
                        У нас в проекте (Календарь Mail.ru) на отдельной странице «API Explorer» (из которой можно совершить любые AJAX-запросы с любыми параметрами) мы с помощью middleware сделали подмешивание всех SQL-запросов в JSON-ответ. В браузере на этой самой странице есть две вкладки: Result (JSON-ответ) и SQL. Соответственно, во второй вкладке видны все запросы и время выполнения каждого из них. Конечно, этот функционал доступен не всем пользователям =)

                        Универсального решения для профилирования SQL запросов в AJAX запросах я не встречал.
                        • 0
                          Жаль, похоже каждый свой велосипед пишет(
                      • НЛО прилетело и опубликовало эту надпись здесь

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое