¿Por qué hay () y todos () ineficaz en el tratamiento de los booleanos?

votos
2

Me he dado cuenta de algo mientras juega con timeity and, or, any(), all()que pensé que podría compartir aquí. Aquí está la secuencia de comandos para medir el rendimiento:

def recursion(n):
    A slow way to return a True or a False boolean.
    return True if n == 0 else recursion(n-1)       

def my_function():
    The function where you perform all(), any(), or, and.
    a = False and recursion(10)

if __name__ == __main__:
    import timeit
    setup = from __main__ import my_function
    print(timeit.timeit(my_function(), setup=setup))

Y aquí hay algunos tiempos:

a = False and recursion(10)
0.08799480279344607

a = True or recursion(10)
0.08964192798430304

Como era de esperar, True or recursion(10)al igual que False and recursion(10)son muy rápidos para calcular porque sólo la primera materia plazo y la operación regresa inmediatamente.

a = recursion(10) or True # recursion() is False
1.4154556830951606 

a = recursion(10) and False # recursion() is True
1.364157978046478

Tener or Trueo and Falseen la línea de no acelerar el cálculo aquí porque son evaluados segundos y toda la recursividad tiene que realizarse en primer lugar. Si bien molesto, es lógico y que sigue las reglas de prioridad operación.

Lo que es más sorprendente es que all(), y any()siempre tienen el peor desempeño, independientemente de procedimiento:

a = all(i for i in (recursion(10), False))) # recursion() is False
1.8326778537880273

a = all(i for i in (False, recursion(10))) # recursion() is False
1.814645767348111

Lo que habría esperado la segunda evaluación a ser mucho más rápido que el primero.

a = any(i for i in (recursion(10), True))) # recursion() is True
1.7959248761901563

a = any(i for i in (True, recursion(10))) # recursion() is True
1.7930442127481

expectativas no satisfechas mismos aquí.

Por lo que parece any()y all()están lejos de ser una forma práctica de escritura, respectivamente un gran ory un gran andsi conseguir un alto rendimiento en su aplicación. ¿Porqué es eso?

Editar: en base a los comentarios parece que la generación de tupla es lento. No veo ninguna razón por la cual en sí Python no podía usar esto:

def all_faster(*args):
    Result = True
    for arg in args:
        if not Result:
            return False
        Result = Result and arg
    return True

def any_faster(*args):
    Result = False
    for arg in args:
        if Result:
            return True
        Result = Result or arg
    return False

Es más rápido ya que las funciones incorporadas y parece tener el mecanismo de cortocircuito.

a = faster_any(False, False, False, False, True)
0.39678611016915966

a = faster_any(True, False, False, False, False)
0.29465180389252055

a = faster_any(recursion(10), False) # recursion() is True
1.5922580174283212

a = faster_any(False, recursion(10)) # recursion() is True
1.5799157924820975

a = faster_all(False, recursion(10)) # recursion() is True
1.6116566893888375

a = faster_all(recursion(10), False) # recursion() is True
1.6004807187900951

Edit2: bien Es más rápido con argumentos que se pasan uno a uno, pero más lento con los generadores.

Publicado el 19/09/2018 a las 13:22
fuente por usuario
En otros idiomas...                            


2 respuestas

votos
2

En realidad, any()es equivalente a una cadena de ory all()es equivalente a una cadena de and, incluyendo cortocircuito. El problema reside en la manera de llevar a cabo el punto de referencia.

Considera lo siguiente:

def slow_boolean_gen(n, value=False):
    for x in range(n - 1):
        yield value
    yield not value

generator = slow_boolean_gen(10)

print([x for x in generator])
# [False, False, False, False, False, False, False, False, False, True]

y los siguientes micro-puntos de referencia:

%timeit generator = slow_boolean_gen(10, True); next(generator) or next(generator) or next(generator) or next(generator) or next(generator) or next(generator) or next(generator) or next(generator) or next(generator) or next(generator)
# 492 ns ± 35.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, False); next(generator) or next(generator) or next(generator) or next(generator) or next(generator) or next(generator) or next(generator) or next(generator) or next(generator) or next(generator)
# 1.18 µs ± 12.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, True); next(generator) and next(generator) and next(generator) and next(generator) and next(generator) and next(generator) and next(generator) and next(generator) and next(generator) and next(generator)
# 1.19 µs ± 11.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, False); next(generator) and next(generator) and next(generator) and next(generator) and next(generator) and next(generator) and next(generator) and next(generator) and next(generator) and next(generator)
# 473 ns ± 6.27 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit generator = slow_boolean_gen(10, True); any(x for x in generator)
# 745 ns ± 15 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, False); any(x for x in generator)
# 1.29 µs ± 12.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, True); all(x for x in generator)
# 1.3 µs ± 22.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, False); all(x for x in generator)
# 721 ns ± 8.05 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit generator = slow_boolean_gen(10, True); any([x for x in generator])
# 1.03 µs ± 28.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, False); any([x for x in generator])
# 1.09 µs ± 27.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, True); all([x for x in generator])
# 1.05 µs ± 11.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, False); all([x for x in generator])
# 1.02 µs ± 11.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Se puede ver claramente que el corto circuito está funcionando, pero si primero se construye el list, que tiene una constante de tiempo que compensa cualquier ganancia en el rendimiento que se obtiene de un cortocircuito.

EDITAR:

Una aplicación manual no nos comprar cualquier ganancia de rendimiento:

def all_(values):
    result = True
    for value in values:
        result = result and value
        if not result:
            break
    return result

def any_(values):
    result = False
    for value in values:
        result = result or value
        if result:
            break
    return result

%timeit generator = slow_boolean_gen(10, True); any_(x for x in generator)
# 765 ns ± 6.76 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, False); any_(x for x in generator)
# 1.48 µs ± 8.97 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, True); all_(x for x in generator)
# 1.47 µs ± 5.71 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit generator = slow_boolean_gen(10, False); all_(x for x in generator)
# 765 ns ± 8.76 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Respondida el 19/09/2018 a las 14:11
fuente por usuario

votos
1

anyy allcortocircuito bien.

El problema es que aquí, en ambos casos, usted tiene que construir la tupleantes de pasarlo a anylo que el orden no hace una diferencia: el tiempo empleado sigue siendo el mismo. Vamos a descomponer esto con una variable:

t = (True, recursion(10))   # recursion is called
a = any(i for i in t)       # this is very fast, only boolean testing

Cuando se está llegando a la segunda línea, ya se ha pasado el tiempo.

Es diferente con ando orcual cortocircuito.

El caso en el que anyo allson interesantes es cuando usted está evaluando los datos cuando se está probando:

any(recusion(x) for x in (10,20,30))

Si usted quiere evitar la evaluación, usted podría pasar una tupla de lambdas (funciones inline) a anyy llamar a las funciones:

ahora:

a = any(i() for i in (lambda:recursion(10), lambda:True))) 

y:

a = any(i() for i in (lambda:True,lambda:recursion(10)))) 

tiene un tiempo de ejecución muy diferente (este último es instantáneo)

Respondida el 19/09/2018 a las 13:23
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more