• [^] # Re: Comparaison avec numpy?

    Posté par . En réponse à la dépêche Numba 0.14. Évalué à 10.

    J'ai réécrit un peu ta fonction :

    def np_mandelbrot(width, height):
     x_min, x_max = -2.0, 1.0
     y_min, y_max = -1.0, 1.0
     stepx = (x_max - x_min) / (width - 1)
     stepy = (y_max - y_min) / (height - 1)
     c = numpy.fromfunction(lambda x,y:
     (x_min + x * stepx) + (y_min + y * stepy) *1j,
     (width, height))
     arr = numpy.zeros((width, height), dtype=complex)
     max_iters = 20
     for i in range(max_iters):
     arr = arr**2 + c
     return abs(arr) <= 2

    Au final elle reste trois fois plus lente :

    >>> timeit.timeit("opt(200, 200)", setup="from __main__ import opt", number=1)
    0.0033650129998932243
    >>> timeit.timeit("mandel.np_mandelbrot(200, 200)", setup="import mandel", number=1)
    0.009533904999443621

    NumPy en soi est très rapide, mais faire des calculs matriciels t'empêche de sortir de la boucle plus tôt quand un point donné diverge rapidement. D'ailleurs ça diverge parfois tellement qu'il y a des dépassements de flottants (!) :

    /home/antoine/numba/mandel.py:39: RuntimeWarning: overflow encountered in square
    /home/antoine/numba/mandel.py:39: RuntimeWarning: invalid value encountered in square
    /home/antoine/numba/mandel.py:40: RuntimeWarning: invalid value encountered in less_equal
    

    Par ailleurs, si tu augmentes la taille du résultat souhaité, l'écart s'élargit (peut-être à cause des caches CPU qui deviennent moins efficaces pour la version matricielle) :

    >>> timeit.timeit("opt(2000, 2000)", setup="from __main__ import opt", number=1)
    0.15761542599921086
    >>> timeit.timeit("mandel.np_mandelbrot(2000, 2000)", setup="import mandel", number=1)
    1.2320581470003162

    Note : on peut faire une version NumPy qui fasse les calculs sur place (sans créer de tableaux intermédiaires). La fin de la fonction s'écrit alors :

    [...]
     for i in range(max_iters):
     numpy.multiply(arr, arr, out=arr)
     numpy.add(arr, c, out=arr)
     numpy.abs(arr, out=arr)
     return arr <= 2

    Elle réduit un peu l'écart :

    >>> timeit.timeit("opt(2000, 2000)", setup="from __main__ import opt", number=1)
    0.15364889400007087
    >>> timeit.timeit("mandel.np_mandelbrot(2000, 2000)", setup="import mandel", number=1)
    0.6474335490001977