TLDR/ Просто дайте мне код:

import time
class timer:
    def __init__(self):
        self.ts, self.te = 0, 0
        
    def __enter__(self):
        self.ts = time.perf_counter()
    def __exit__(self, *args, **kwargs):
        self.te = time.perf_counter()
        dt = self.te-self.ts
        print(f"this took {dt // 60:.2f} min {dt % 60:.2f} sec")

Как специалисты по данным, мы заботимся о производительности. К сожалению, python не особенно быстр сам по себе. За простоту использования приходится платить медлительностью, если мы попытаемся выполнить некоторые операции наивно.

К счастью, существует множество отличных библиотек, таких как numpy и scikit-learn, которые облегчают нашу жизнь. Эти библиотеки переносят высокопроизводительные подпрограммы на более эффективные языки, такие как C или C++, чтобы мы могли выполнять быстрые математические операции. Например, если мы хотим иметь все ненулевые элементы некоторого большого массива, то numpy.nonzero(array) обеспечивает очень быстрый способ достижения этого.

array = np.random.choice([1,0],size=10**8).reshape((10**2,10**2,10**2, 10**2))
coords = np.nonzero(array)

Поэтому нам нужно проверить, действительно ли код, который мы написали, быстр. Есть много способов добиться этого. У нас есть модуль timeit, который, вероятно, является одним из лучших вариантов, поскольку он дает вам «статистический ответ», выполняя ваш фрагмент кода несколько раз.

dt = timeit.timeit(lambda:np.nonzero(array), number=10) / 10
print(f"This took {dt // 60:.2f} min {dt % 60:.2f} sec")
OUT: This took 0.00 min 2.11 sec

Однако его интерфейс не самый простой, так как вам нужно обернуть код, который вы хотите протестировать, в функцию и передать его в timeit.timeit…

Другой вариант, возможно, более подходящий при простом прототипировании в блокнотах Jupyter, — это модуль времени. Метод time.perf_counter() возвращает отметку времени, которую можно использовать для оценки вашего кода. Вам нужно только вызвать его до и после целевых строк, которые вы хотите проверить, сохранить время начала и время окончания в некоторых переменных и вычислить разницу во времени.

t_start = time.perf_counter()
coords = np.nonzero(array)
t_end = time.perf_counter()
dt = t_end - t_start
print(f"This took {dt // 60:.2f} min {dt % 60:.2f} sec")
OUT: This took 0.00 min 2.20 sec

Однако мне особенно скучно вставлять операторы t_start = time.perf_counter(), t_end=time.perf_counter() и print(…) каждый раз, когда я хочу измерить, сколько времени это занимает. Вот почему я нашел элегантный способ обойти эту проблему.

Мое решение относится к операторам Python «with», изначально использовавшимся для чтения и записи внешних файлов. Однако они использовались для других целей, таких как включение или отключение механизма автоградации Pytorch. Например, если мы хотим вычислить некоторые показатели, используя выходные данные нейронной сети во время обучения, но не вмешиваясь в граф вычислений, мы пишем «с torch.no_grad():», а затем соответствующие строки кода ниже этого оператора.

Поэтому я хотел иметь что-то подобное, но с использованием метода time.perf_counter, только ради быстрого прототипирования в Jupyter Notebooks или аналогичных средах. Что-то, что позволяет нам ввести «с timeit():», а затем ваш код, а затем мы запустим его и посмотрим, сколько времени это заняло. Для этого нам нужно реализовать класс, реализующий два специальных метода: __enter__() и __exit__().

Там идет:

import time
class timer:
    def __init__(self):
        self.ts, self.te = 0, 0
        
    def __enter__(self):
        self.ts = time.perf_counter()
    def __exit__(self, *args, **kwargs):
        self.te = time.perf_counter()
        dt = self.te-self.ts
        print(f"this took {dt // 60:.2f} min {dt % 60:.2f} sec")

Что приводит к более чистому использованию:

with timer():
    coords = np.nonzero(array)
OUT: this took 0.00 min 2.07 sec

Я надеюсь, что это может быть так же полезно для вас, как и для меня.

Увидимся в следующий раз!