Передача итераторов любому для выполнения для скорости и почему?

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

  1. Это вообще хорошая идея? (Это , а не ниже)
  2. Интересно, действительно ли карта добавляет улучшения скорости? Почему?
  3. Почему передача итераторов в любой ускоряет мой код?
  4. Почему мой объект Counter работал, а функция print_true с треском провалилась?
  5. Есть ли эквивалент itertools.imap , который будет просто вызывать функцию снова и снова, и, возможно, определенное количество раз?
  6. Где моя морковь?!?

I только что посмотрел PyCon 2011: Как это сделал Dropbox и как Python помог (правда, я пропустил большую часть частей), но НАКОНЕЦ действительно интересный материал начался примерно в 22:23.

Выступающий высказался за создание ваших внутренних циклов на C, и этот «запускать один раз» не требует особой оптимизации (имеет смысл) ... затем он продолжает утверждать ... перефразируя:

Передайте композицию из итераторы в любой для значительного повышения скорости.

Вот код (надеюсь, он идентичный):

import itertools, hashlib, time   
_md5 = hashlib.md5()  
def run():
    for i in itertools.repeat("foo", 10000000):
        _md5.update(i)
a = time.time();  run(); time.time() - a  
Out[118]: 9.44077205657959

_md5 = hashlib.md5() 
def run():
    any(itertools.imap(_md5.update, itertools.repeat("foo", 10000000)))    
a = time.time();  run(); time.time() - a
Out[121]: 6.547091007232666

Хмм, похоже, для еще большего увеличения скорости я могу просто получить более быстрый компьютер! (Судя по его слайду.)

Затем он несколько раз машет руками, не вдаваясь в подробности , почему .

Я уже знал об итераторах из ответа на питонический способ сделать что-то N раз без индексной переменной? благодаря Алексу Мартелли.

Тогда я подумал: интересно, действительно ли карта добавляет улучшения скорости? Моя последняя мысль была WTF ??? переход на любой ? В САМОМ ДЕЛЕ??? Конечно, это не может быть правильным, поскольку в документации любой определяется как:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

Почему в мире передача итераторов в любой делает мой код быстрее?

Затем я протестировал его, используя следующее (среди многих другие тесты), но вот что меня подводит:

def print_true(x):
    print 'True'
    return 'Awesome'

def test_for_loop_over_iter_map_over_iter_repeat():
    for result in itertools.imap(print_true, itertools.repeat("foo", 5)):
        pass

def run_any_over_iter_map_over_iter_repeat():
    any(itertools.imap(print_true, itertools.repeat("foo", 5)))

And the runs:

    In [67]: test_for_loop_over_iter_map_over_iter_repeat()
    True
    True
    True
    True
    True

    In [74]: run_any_over_iter_map_over_iter_repeat()
    True

К стыду. Я заявил, что этот ПАРЕНЬ ЕГО ПОЛНЫЙ. Еретик ! Но я успокоился и продолжил тестировать. Если бы это было правдой, как, черт возьми, Dropbox мог бы вообще работать!?!?

И после дальнейшего тестирования он действительно работал ... Сначала я просто использовал простой объект счетчика, и он насчитал до 10 000 000 в обоих случаях.

Итак, вопрос в том, почему мой объект Counter работал, а моя функция print_true с треском провалилась?

class Counter(object):
    count = 0
    def count_one(self, none):
        self.count += 1

def run_any_counter():
    counter = Counter()
    any(itertools.imap(counter.count_one, itertools.repeat("foo", 10000000)))
    print counter.count

def run_for_counter():
    counter = Counter()
    for result in itertools.imap(counter.count_one, itertools.repeat("foo", 10000000)):
        pass
    print counter.count

вывод:

%time run_for_counter()
10000000
CPU times: user 5.54 s, sys: 0.03 s, total: 5.57 s
Wall time: 5.68 s

%time run_any_counter()
10000000
CPU times: user 5.28 s, sys: 0.02 s, total: 5.30 s
Wall time: 5.40 s

WTF еще больше даже после удаления ненужного аргумента и написания наиболее разумного кода для моего объекта Counter, он ВСЕ ЕЩЕ медленнее, чем версия с любой картой. Где моя морковь?!?:

class CounterNoArg(object):
    count = 0
    def count_one(self):
        self.count += 1

def straight_count():
    counter = CounterNoArg()
    for _ in itertools.repeat(None, 10000000):
        counter.count_one()
    print counter.count

Ouput:

In [111]: %time straight_count()
10000000
CPU times: user 5.44 s, sys: 0.02 s, total: 5.46 s
Wall time: 5.60 s

Я спрашиваю, потому что думаю, что питонистам или питонистам нужна морковь, поэтому мы не начинаем передавать вещи any или all для увеличения производительности, или он уже существует? Возможно, эквивалент itertools.imap , который будет просто вызывать функцию снова и снова, и, возможно, определенное количество раз.

Лучшее, что мне удалось, это (использование списка дает интересные результаты):

def super_run():
    counter = CounterNoArg()
    for _ in (call() for call in itertools.repeat(counter.count_one, 10000000)):
        pass
    print counter.count

def super_counter_run():
    counter = CounterNoArg()
    [call() for call in itertools.repeat(counter.count_one, 10000000)]
    print counter.count

def run_any_counter():
    counter = Counter()
    any(itertools.imap(counter.count_one, itertools.repeat("foo", 10000000)))
    print counter.count

%time super_run()
10000000
CPU times: user 5.23 s, sys: 0.03 s, total: 5.26 s
Wall time: 5.43 s

%time super_counter_run()
10000000
CPU times: user 4.75 s, sys: 0.18 s, total: 4.94 s
Wall time: 5.80 s

%time run_any_counter()
10000000
CPU times: user 5.15 s, sys: 0.06 s, total: 5.21 s
Wall time: 5.30 s

def run_any_like_presentation():
    any(itertools.imap(_md5.update, itertools.repeat("foo", 10000000)))

def super_run_like_presentation():
    [do_work for do_work in itertools.imap(_md5.update, itertools.repeat("foo", 10000000))]

def super_run_like_presentation_2():
    [_md5.update(foo) for foo in itertools.repeat("foo", 10000000)]


%time run_any_like_presentation()
CPU times: user 5.28 s, sys: 0.02 s, total: 5.29 s
Wall time: 5.47 s

%time super_run_like_presentation()
CPU times: user 6.14 s, sys: 0.18 s, total: 6.33 s
Wall time: 7.56 s

%time super_run_like_presentation_2()
CPU times: user 8.44 s, sys: 0.22 s, total: 8.66 s
Wall time: 9.59 s

Ух ...

Примечание: я рекомендую вам провести тесты самостоятельно.

5
задан John R Perry 24 December 2017 в 15:22
поделиться