LINUX.ORG.RU

Производительность в python: built-in any/all VS самописный цикл

 


1

6

Заметил забавный факт и никак не могу понять в чем дело. any / all работают медленнее чем такой же самописный цикл. Для примера возьмем задачу: проверить, подходит ли строка под шаблон '[a-Z_]\w+'. Те является ли она корректным именем переменной.

#!/usr/bin/env python3

import re
import string

regexp1 = re.compile(r'^[a-z_]\w*$', re.IGNORECASE)
symbols = frozenset(string.ascii_letters + string.digits + '_')


def check1(name):
    return regexp1.search(name) is not None


def check2(name):
    first_ch = name[0]
    if first_ch >= '0' and first_ch <= '9':
        return False

    for ch in name:
        if ch not in symbols:
            return False

    return True


def check3(name):
    first_ch = name[0]
    if first_ch >= '0' and first_ch <= '9':
        return False

    return all(ch in symbols for ch in name)


def check_speed(fname, name, count):
    from timeit import timeit
    time = timeit('{0}("{1}")'.format(fname, name), 'from __main__ import {0}'.format(fname), number=count)
    print('{0}: {1:.3f} seconds in {2} iterations for "{3}"'.format(fname, time, count, name))


def main():
    import sys
    fnames = ['check1', 'check2', 'check3']
    name = sys.argv[1]
    count = int(sys.argv[2])
    for fname in fnames:
        check_speed(fname, name, count)


if __name__ == '__main__':
    main()

При этом получаем интересные результаты, на фоне которых интересно выделяется отставание примера с all

[actics@6910p trash]$ ./p.py "a12" 1000000
check1: 4.822 seconds in 1000000 iterations
check2: 2.143 seconds in 1000000 iterations
check3: 4.985 seconds in 1000000 iterations

[actics@6910p trash]$ ./p.py "a12345678901234567890" 1000000
check1: 6.784 seconds in 1000000 iterations
check2: 5.328 seconds in 1000000 iterations
check3: 10.320 seconds in 1000000 iterations

[actics@6910p trash]$ ./p.py "a12345678901234567890123456789012345678901234567890" 1000000
check1: 9.881 seconds in 1000000 iterations
check2: 11.759 seconds in 1000000 iterations
check3: 19.880 seconds in 1000000 iterations

Результаты для первого и второго теста очевидны: чем больше вход тем меньше сказываются накладные расходы на инициализацию автомата в регекспе, поэтому check1 начинает уделывать check2. А вот результаты с all мне абсолютно не понятны: в чем может быть дело? В использовании генератора? Проверил, если передавать массив набитый булами all начинает ускоряться. Получается, что с генераторами лучше велосипедить цикл? Или дело в чем-то другом?



Последнее исправление: actics (всего исправлений: 2)

это оверхед на создание генератора

iSlava
()

any в цикле вызывает метод next() грубо говоря. Вызов питоновского метода из сишного кода это дольше чем одна итерация питоновского кода.

redixin ★★★★
()
Ответ на: комментарий от redixin

Тут дело даже наверно не в этом. Из сишного кода дорого вызывать питоньи функции, а в данном примере, получается, неявно используется lambda

actics
() автор топика
Ответ на: комментарий от Gorthauer

Он не умеет инлайнить байткод внутрь нативного кода.

redixin ★★★★
()
Ответ на: комментарий от redixin

Странная мысль в голову тогда пришла, но сейчас кажется что это глупо. Подумал что ch in symbols может оборачиваться в лямбду.

actics
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.