Cómo utilizar el módulo timeit

Entiendo el concepto de lo que timeit no, pero no estoy seguro de cómo ponerlo en práctica en mi código.

¿Cómo puedo comparar las dos funciones, decir insertion_sort y tim_sort, con timeit?

InformationsquelleAutor Neemaximo | 2011-11-22

14 Kommentare

  1. 258

    La manera timeit obras es ejecutar el programa de instalación de código una vez y, a continuación, hacer varias llamadas a una serie de declaraciones. Por lo tanto, si usted desea probar la clasificación, un poco de cuidado es necesario para que uno pase en una especie no afecta el paso siguiente ya con los datos ordenados (que, por supuesto, haría que la Timsort realmente brillan porque funciona mejor cuando los datos ya parcialmente ordenado).

    Aquí es un ejemplo de cómo configurar una prueba para la clasificación:

    >>> import timeit
    
    >>> setup = '''
    import random
    
    random.seed('slartibartfast')
    s = [random.random() for i in range(1000)]
    timsort = list.sort
    '''
    
    >>> print min(timeit.Timer('a=s[:]; timsort(a)', setup=setup).repeat(7, 1000))
    0.334147930145

    Nota que la serie de declaraciones de hace una nueva copia de los datos sin ordenar en cada pasada.

    También, tenga en cuenta el tiempo de la técnica de correr la medición de la suite de siete veces y manteniendo sólo el mejor tiempo — esto realmente puede ayudar a reducir la medición de las distorsiones debidas a otros procesos que se ejecutan en su sistema.

    Esos son mis consejos para el uso de timeit correctamente. Espero que esta ayuda 🙂

    • No el tiempo que se muestra incluye el tiempo para copiar la lista en la declaración de a=s[:]? ¿Qué harías si quería llegar puramente a la ordenación del tiempo?
    • Sí, se incluye la lista de copia (que es muy rápida en comparación con la clase en sí). Si no copia a pesar de que, en el primer pase se ordena la lista y el resto de pasado no tiene que hacer ningún trabajo. Si quieres saber el momento justo para la ordenación, a continuación, ejecute el anterior con y sin el timsort(a) y tomar la diferencia 🙂
    • Me gustaría recomendar a repetir 7 veces para cada instalación, y a la media; en lugar de al revés. De esta manera, si cada pico debido a otros procesos tiene una buena oportunidad de ser ignorado por completo, en lugar de un promedio.
    • Utilice el min() en lugar de la media de los tiempos. Que es una recomendación de mí, de Tim Peters, y de Guido van Rossum. El tiempo más rápido representa lo mejor de un algoritmo puede realizar cuando el caché está cargado y el sistema no está ocupado con otras tareas. Todos los tiempos son ruidosas-el mejor tiempo es el menos ruidoso. Es fácil mostrar que la forma más rápida de tiempo es el más reproducible y por lo tanto el más útil cuando el tiempo dos implementaciones diferentes.
    • Se calcula una media (bueno, total, pero es equivalente) para las 1000 entradas; a continuación, repita 7 veces, y tomar el mínimo. Usted necesita el promedio de más de 1000 entradas, porque desea que el promedio (no el mejor de los casos) la complejidad de los algoritmos. Usted necesita el mínimo de, precisamente, la razón por la que usted dio. He pensado que puede mejorar su enfoque mediante la elección de una entrada, se ejecuta el algoritmo de 7 veces, tomando el mínimo; luego repetir por 1000 entradas diferentes, y tomando el promedio. Lo que no sabía es que su .repeat(7,1000) ya lo hace (por usar la misma semilla)! Así que la solución es perfecto OMI.
    • Sólo puedo añadir que la forma de asignar el presupuesto de 7000 ejecuciones (por ejemplo, .repeat(7, 1000) vs .repeat(2, 3500) vs .repeat(35, 200) debería depender de cómo el error debido a la carga del sistema se compara con el error debido a la entrada de la variabilidad. En el caso extremo, si el sistema está siempre en condiciones de carga pesada, y ves una cola larga y fina en la parte izquierda de la ejecución de la distribución en el tiempo (cuando lo coges en un raro estado de reposo), usted podría incluso encontrar .repeat(7000,1) ser más útil que la .repeat(7,1000) si no puedes presupuesto de más de 7000 pistas.
    • Cómo acerca de la duplicación de la matriz ya en la instalación, la creación de un iterador it sobre ellos, y entonces la sincronización 'a=next(it); timsort(a)'?

  2. 264

    Si quieres usar timeit en un sistema interactivo de Python sesión, hay dos opciones convenientes:

    1. Utilizar el IPython shell. Cuenta con la conveniente %timeit función especial:

      In [1]: def f(x):
         ...:     return x*x
         ...: 
      
      In [2]: %timeit for x in range(100): f(x)
      100000 loops, best of 3: 20.3 us per loop
    2. En un estándar de la intérprete de Python, puede acceder a las funciones y los otros nombres que se definió anteriormente durante la sesión interactiva importándolos de __main__ en la instrucción de instalación:

      >>> def f(x):
      ...     return x * x 
      ... 
      >>> import timeit
      >>> timeit.repeat("for x in range(100): f(x)", "from __main__ import f",
                        number=100000)
      [2.0640320777893066, 2.0876040458679199, 2.0520210266113281]
    • +1 para mostrar el from __main__ import f técnica. Yo no creo que esto es tan conocidos, como debe ser. Es útil en casos como este, donde una función o método de llamada está siendo programado. En otros casos (tiempo una serie de pasos), es menos útil, ya que introduce la función de la sobrecarga de la llamada.
    • Usted puede hacer %timeit f(x)
    • Nota: la importación «f» el programa de instalación hace que el acceso a f una rápida lectura local – que no es exactamente reflejan una función global de la llamada (de corto función rápida) en la normal de código. En Py3.5+ real globales pueden ser suministrados: «Cambió en la versión 3.5: opcional globals parámetro se agregó.»; Antes de que los globales de la timeit módulo donde inevitable (que no tiene mucho sentido). Posiblemente los globales de código que realiza la llamada (sys._getframe(N).f_globals) debería haber sido el predeterminado desde el principio.
  3. 130

    Voy a dejar un secreto: la mejor manera de utilizar timeit está en la línea de comandos.

    En la línea de comandos, timeit hace adecuado análisis estadístico: se le dice a la cantidad de tiempo que el tiraje se llevó. Esto es bueno porque todos error en la sincronización es positivo. Así el menor tiempo tiene el menor error. No hay manera de conseguir error negativo porque un equipo que nunca podemos calcular más rápido de lo que se puede calcular!

    Así, la interfaz de línea de comandos:

    %~> python -m timeit "1 + 2"
    10000000 loops, best of 3: 0.0468 usec per loop

    Que es bastante simple, ¿eh?

    Puede configurar cosas:

    %~> python -m timeit -s "x = range(10000)" "sum(x)"
    1000 loops, best of 3: 543 usec per loop

    que es útil, también!

    Si quieres tener varias líneas, puede utilizar el shell de continuación automática o el uso de argumentos separados:

    %~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
    1000 loops, best of 3: 554 usec per loop

    Que le da una configuración de

    x = range(1000)
    y = range(100)

    y a veces

    sum(x)
    min(y)

    Si quieres tener más secuencias de comandos que usted podría estar tentado a pasar a timeit dentro de una secuencia de comandos de Python. Sugiero evitar que debido a que el análisis y el tiempo es simplemente mejor en la línea de comandos. En lugar de eso, tiendo a hacer scripts de shell:

     SETUP="
    
     ... # lots of stuff
    
     "
    
     echo Minmod arr1
     python -m timeit -s "$SETUP" "Minmod(arr1)"
    
     echo pure_minmod arr1
     python -m timeit -s "$SETUP" "pure_minmod(arr1)"
    
     echo better_minmod arr1
     python -m timeit -s "$SETUP" "better_minmod(arr1)"
    
     ... etc

    Esto puede tomar un poco más de tiempo debido a las múltiples inicializaciones, pero normalmente eso no es una gran cosa.


    Pero lo que si usted quiere utilizar timeit dentro de su módulo?

    Bien, la forma más sencilla es hacer:

    def function(...):
        ...
    
    timeit.Timer(function).timeit(number=NUMBER)

    y que le da acumulativa (no mínimo!) tiempo para ejecutar ese número de veces.

    Para obtener un buen análisis, uso .repeat y tener el mínimo:

    min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))

    Normalmente se debe combinar esto con functools.partial en lugar de lambda: ... para reducir la sobrecarga. Por lo tanto usted podría tener algo como:

    from functools import partial
    
    def to_time(items):
        ...
    
    test_items = [1, 2, 3] * 100
    times = timeit.Timer(partial(to_time, test_items)).repeat(3, 1000)
    
    # Divide by the number of repeats
    time_taken = min(times) / 1000

    También puede hacer:

    timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)

    que le daría algo más cercano a la interfaz desde la línea de comandos, pero es mucho menos fresco manera. El "from __main__ import ..." le permite utilizar el código del módulo principal dentro del ambiente artificial creado por timeit.

    Vale la pena señalar que esta es una comodidad contenedor para Timer(...).timeit(...) y así no es particularmente bueno en el tiempo. Yo personalmente ahora prefieren utilizar Timer(...).repeat(...) como he mostrado anteriormente.


    Advertencias

    Hay un par de advertencias con timeit que mantenga todas partes.

    • Sobrecarga no está contabilizado. Dice que quiere tiempo x += 1, para saber cuánto tiempo además de la toma:

      >>> python -m timeit -s "x = 0" "x += 1"
      10000000 loops, best of 3: 0.0476 usec per loop

      Bueno, es no 0.0476 µs. Sólo se sabe que es menos que eso. Todo error es positivo.

      Así que intenta encontrar puro sobrecarga:

      >>> python -m timeit -s "x = 0" ""      
      100000000 loops, best of 3: 0.014 usec per loop

      Que una buena 30% sobrecarga justo de tiempo! Esto puede masivamente desfase relativo de los tiempos. Pero en realidad sólo se preocupaba por la la adición de tiempos; la mirada-hasta los tiempos de x también necesitan ser incluidos en los gastos generales:

      >>> python -m timeit -s "x = 0" "x"
      100000000 loops, best of 3: 0.0166 usec per loop

      La diferencia no es muy grande, pero está ahí.

    • La mutación de los métodos son peligrosos.

      >>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
      10000000 loops, best of 3: 0.0436 usec per loop

      Pero que completamente equivocado! x es la lista vacía después de la primera iteración. Deberá reinicializar:

      >>> python -m timeit "x = [0]*100000" "while x: x.pop()"
      100 loops, best of 3: 9.79 msec per loop

      Pero entonces usted tiene un montón de sobrecarga. Cuenta de que por separado.

      >>> python -m timeit "x = [0]*100000"                   
      1000 loops, best of 3: 261 usec per loop

      Nota que resta de la sobrecarga es razonable aquí sólo porque la sobrecarga es un pequeño-ish fracción del tiempo.

      Por su ejemplo, vale la pena señalar que tanto de Ordenación por Inserción y Tim Tipo se han totalmente inusual de temporización comportamientos para ya-listas ordenadas. Esto significa que usted va a requerir un random.shuffle entre clases si desea evitar la demolición de sus tiempos.

    • ¿qué usec decir? es microsegundos?
    • Sí.
    • Porque no intentar muestra varias veces.
    • Los lectores de esta respuesta también podría estar interesado en el Uso de Python timeit de un programa, pero funciona de la misma manera como la línea de comandos?.
    • Teniendo en cuenta que la declaración de restar puro de temporización de la sobrecarga, timeit ejecuta un pass declaración cuando no se da ningún argumento, que, por supuesto, lleva algo de tiempo. Si cualquiera de los argumentos que se dan, pass se no se ejecuta, por lo que restando algunos 0.014 usecs de cada momento preciso sería incorrecto.
    • es casi gratis a ejecutar; el punto es que para medir el benchmarking aparato.
    • ¿Estás seguro de que lo que usted dice es el momento en que el aparato no es el tiempo de ejecución de pass?
    • Bastante confianza, sí.
    • Tienes razón, acabo de probar python -m 'pass' 'pass' y era tan rápida como una sola. Huy, tal vez debería haber pensado en eso antes.
    • Sólo para detener a nadie, usted también puede hacer time python script.py a tiempo, toda una secuencia de comandos desde la línea de comandos

  4. 89

    Si quieres comparar dos bloques de código /rápido a las funciones que usted podría hacer:

    import timeit
    
    start_time = timeit.default_timer()
    func1()
    print(timeit.default_timer() - start_time)
    
    start_time = timeit.default_timer()
    func2()
    print(timeit.default_timer() - start_time)
  5. 41

    Me parece la manera más fácil de utilizar timeit es desde la línea de comandos:

    Dado test.py:

    def InsertionSort(): ...
    def TimSort(): ...

    ejecutar timeit como este:

    % python -mtimeit -s'import test' 'test.InsertionSort()'
    % python -mtimeit -s'import test' 'test.TimSort()'
  6. 15

    para mí, esta es la forma más rápida:

    import timeit
    def foo():
        print("here is my code to time...")
    
    
    timeit.timeit(stmt=foo, number=1234567)
  7. 11
    # Генерация целых чисел
    
    def gen_prime(x):
        multiples = []
        results = []
        for i in range(2, x+1):
            if i not in multiples:
                results.append(i)
                for j in range(i*i, x+1, i):
                    multiples.append(j)
    
        return results
    
    
    import timeit
    
    # Засекаем время
    
    start_time = timeit.default_timer()
    gen_prime(3000)
    print(timeit.default_timer() - start_time)
    
    # start_time = timeit.default_timer()
    # gen_prime(1001)
    # print(timeit.default_timer() - start_time)
  8. 7

    Esto funciona muy bien:

      python -m timeit -c "$(cat file_name.py)"
    • ¿Cuál sería el equivalente en Windows?
    • ¿Cómo pasar parámetros, si el guión lo requiere ninguna?
  9. 3

    permite la instalación de la misma diccionario en cada uno de los siguientes y la prueba de que el tiempo de ejecución.

    La instalación argumento es básicamente la configuración del diccionario

    Número es ejecutar el código 1000000 de veces. No la configuración, pero la sentencia

    Al ejecutar este se puede ver que el índice es la forma más rápida de obtener. Puede ejecutar varias veces a ver.

    El código básicamente se trata de obtener el valor de c en el diccionario.

    import timeit
    
    print('Getting value of C by index:', timeit.timeit(stmt="mydict['c']", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))
    print('Getting value of C by get:', timeit.timeit(stmt="mydict.get('c')", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))

    Aquí están mis resultados, la suya será diferente.

    por índice: 0.20900007452246427

    por llegar: 0.54841166886888

  10. 3

    simplemente pasar toda su código como un argumento de timeit:

    import timeit
    
    print(timeit.timeit("""
    
    limit = 10000
    prime_list = [i for i in range(2, limit+1)]
    
    for prime in prime_list:
        for elem in range(prime*2, max(prime_list)+1, prime):
            if elem in prime_list:
                prime_list.remove(elem)"""
    
    , number=10))
  11. 0

    La incorporada en el timeit módulo funciona mejor desde el IPython de la línea de comandos.

    A las funciones de tiempo dentro de un módulo:

    from timeit import default_timer as timer
    import sys
    
    def timefunc(func, *args, **kwargs):
        """Time a function. 
    
        args:
            iterations=3
    
        Usage example:
            timeit(myfunc, 1, b=2)
        """
        try:
            iterations = kwargs.pop('iterations')
        except KeyError:
            iterations = 3
        elapsed = sys.maxsize
        for _ in range(iterations):
            start = timer()
            result = func(*args, **kwargs)
            elapsed = min(timer() - start, elapsed)
        print(('Best of {} {}(): {:.9f}'.format(iterations, func.__name__, elapsed)))
        return result
  12. 0

    Ejemplo de cómo el uso de Python REPL intérprete con la función que acepta parámetros.

    >>> import timeit                                                                                         
    
    >>> def naive_func(x):                                                                                    
    ...     a = 0                                                                                             
    ...     for i in range(a):                                                                                
    ...         a += i                                                                                        
    ...     return a                                                                                          
    
    >>> def wrapper(func, *args, **kwargs):                                                                   
    ...     def wrapper():                                                                                    
    ...         return func(*args, **kwargs)                                                                  
    ...     return wrapper                                                                                    
    
    >>> wrapped = wrapper(naive_func, 1_000)                                                                  
    
    >>> timeit.timeit(wrapped, number=1_000_000)                                                              
    0.4458435332577161                                                                                        
  13. 0

    Debe crear dos funciones y, a continuación, ejecute algo similar a esto.
    Aviso, usted desea elegir el mismo número de ejecución/ejecutar para comparar la manzana a manzana.

    Esto fue probado en Python 3.7.

    Cómo utilizar el módulo timeit
    Aquí está el código para la facilidad de copiar

    !/usr/local/bin/python3
    import timeit
    
    def fibonacci(n):
        """
        Returns the n-th Fibonacci number.
        """
        if(n == 0):
            result = 0
        elif(n == 1):
            result = 1
        else:
            result = fibonacci(n-1) + fibonacci(n-2)
        return result
    
    if __name__ == '__main__':
        import timeit
        t1 = timeit.Timer("fibonacci(13)", "from __main__ import fibonacci")
        print("fibonacci ran:",t1.timeit(number=1000), "milliseconds")

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea