Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Loop trop gourmande en mémoire ?

+6 votes

Suite à une question que j'avais posée, j'ai commencé à profiler mon programme http://indexerror.net/634/benchmarker-son-programme. J'utilise donc le module memory_profiler.

J'ai cette méthode:

@profile
def checkFuturesRunning(self):

    total_futures = self.list_futures_images + self.list_futures_urls
    states_futures = []

    for result in total_futures:
        if type(result) is bool:
            states_futures.append(result)
        else:
            states_futures.append(result.done())

    if False not in states_futures and len(total_futures) == len(self.feed.entries) * 2:
        return True
    else:
        return False

Je l'utilise pour vérifier si toutes les futures que j'ai lancées sont finies.
Je démarre une future comme ça (c'est carrément basé sur un des articles du blog S&M):

from requests_futures.sessions import FuturesSession
... Some Code ...
session = FuturesSession(max_workers=20)
future = session.get(url, timeout=10)
self.list_futures_urls.append(future)
future.add_done_callback(aCallBack)

Et quand je lance mon programme avec le profiler sur la méthode du dessus, j'obtiens ça:

Line #    Mem usage    Increment   Line Contents
================================================
   299    639.9 MiB      0.0 MiB       @profile
   300                                 def checkFuturesRunning(self):
   301                             
   302    639.9 MiB      0.0 MiB           total_futures = self.list_futures_images + self.list_futures_urls
   303    639.9 MiB      0.0 MiB           states_futures = []
   304                             
   305    737.9 MiB     97.9 MiB           for result in total_futures:
   306    737.7 MiB     -0.2 MiB               if type(result) is bool:
   307    737.7 MiB      0.0 MiB                   states_futures.append(result)
   308                                         else:
   309    699.3 MiB    -38.3 MiB                   states_futures.append(result.done())
   310                             
   311    737.9 MiB     38.6 MiB           if False not in states_futures and len(total_futures) == len(self.feed.entries) * 2:
   312                                         return True
   313                                     else:
   314    738.1 MiB      0.2 MiB               return False

Alors bon, je ne suis pas un expert, donc je ne sais pas trop quoi en penser. J'ai l'impression que l'entrée dans la boucle me coûte presque 100 MiB. C'est beaucoup, je ne comprends pas pourquoi.

Est-ce que vous pourriez m'éclairer ? Parce que ça fait un sacré trou dans la ram ça quand même.

EDIT réponse à Sam:

@Sam, j'ai essayé ta solution. Je ne la comprends pas très bien, mais quoi qu'il en soit, j'ai le même problème. Au début de la nouvelle boucle, grosse demande en RAM.

Line #    Mem usage    Increment   Line Contents
================================================
   319    803.8 MiB      0.0 MiB       @profile
   320                                 def checkFuturesRunning(self):
   321                             
   322                                     """Method to check if some futures are still running.
   323                                     Returns True if all the futures are done"""
   324                             
   325                                     # total_futures = self.list_futures_images + self.list_futures_urls
   326                                     # states_futures = []
   327                             
   328                                     # for result in total_futures:
   329                                         # if type(result) is bool:
   330                                             # states_futures.append(result)
   331                                         # else:
   332                                             # states_futures.append(result.done())
   333                             
   334                                     # if False not in states_futures and len(total_futures) == len(self.feed.entries) * 2:
   335                                         # return True
   336                                     # else:
   337                                         # return False
   338                             
   339                             
   340                                     # On ne créé pas une nouvelle liste avec la somme des deux listes,
   341                                     # on itère juste sur les deux chainées
   342    809.2 MiB      5.4 MiB           total_futures = itertools.chain(self.list_futures_images, self.list_futures_urls)
   343                             
   344    812.3 MiB      3.1 MiB           no_false_result = False
   345    966.4 MiB    154.1 MiB           for i, result in enumerate(total_futures):
   346    965.9 MiB     -0.5 MiB               result = result if type(result) is bool else result
   347    966.2 MiB      0.3 MiB               if not result:
   348                                             no_false_result = True
   349                             
   350    966.4 MiB      0.3 MiB           return no_false_result and i == len(self.feed.entries) * 2
demandé 4-Avr-2015 par Rififi (532 points)
edité 17-Avr-2015 par Rififi

2 Réponses

+1 vote

Comme ça c'est difficile à évaluer, mais tu peux économiser pas mal de mémoire en faisant :

import itertools

@profile
def checkFuturesRunning(self):

   # On ne créé pas une nouvelle liste avec la somme des deux listes,
   # on itère juste sur les deux chainées
   total_futures = itertools.chain(self.list_futures_images, self.list_futures_urls)

   no_false_result = False
   for i, result in enumerate(total_futures):
      result = result if type(result) is bool else result
      if not result:
         no_false_result = True

    return no_false_result and i == len(self.feed.entries) * 2

Rien à voir, mais checkFuturesRunning serait plus proche du PEP 8 écrite check_futures_running

répondu 9-Avr-2015 par Sam (4,984 points)

Désolé pour le retard, j'ai été débordé. J'ai édité ma question pour te montrer le résultat de ta proposition.

Bon je viens de m'apercevoir d'un truc, c'est que des fois avec memory_profiler, le coût en RAM affiché au début de la boucle est la RAM utilisée DANS la boucle. Donc le problème peut tout autant venir de ce qu'il y a dans la boucle. Je ne vois pas quoi cela dit.

0 votes

Essaie peut-être ceci :

@profile
def checkFuturesRunning(self):
    total_futures = itertools.chain(self.list_futures_images, self.list_futures_urls)
    total_len = 0

    for result in total_futures:
        ended = result if type(result) is bool else result.done()
        if not ended:    # si possible, on quitte au plus tôt
            return False
        total_len += 1

    return total_len == len(self.feed.entries) * 2
répondu 26-Avr-2015 par anonyme
...