Question

J'ai une fonction coûteuse qui prend et renvoie une petite quantité de données (quelques entiers et flotteurs). J'ai déjà memoized cette fonction, mais je voudrais faire la note persistante. Il y a déjà quelques discussions ayant trait à cela, mais je ne suis pas sûr de problèmes potentiels avec certaines des approches proposées, et j'ai des exigences assez spécifiques:

  • Je vais certainement utiliser la fonction de plusieurs threads et processus simultanément (à la fois en utilisant multiprocessing et de scripts python séparés)
  • Je ne vais pas besoin de lire ou d'un accès en écriture à la note de service en dehors de cette fonction python
  • Je ne suis pas préoccupé par la note de service étant corrompu de rares occasions (comme tirant sur la fiche ou d'écrire accidentellement le fichier sans le verrouiller) car il est pas que coûteux à reconstruire (généralement 10 -20 minutes) mais je préférerais que ce ne serait pas corrompu en raison des exceptions, ou mettre fin à un processus manuellement python (je ne sais pas comment réaliste qui est)
  • Je préfère fortement des solutions qui ne nécessitent pas de grandes bibliothèques externes que j'ai un montant fortement limité l'espace disque dur sur une machine que je vais courir le code sur
  • J'ai une faible préférence pour le code multi-plateforme, mais je vais probablement utiliser seulement cela sous Linux

Ce fil traite du module shelve, qui est apparemment pas processus de sécurité. Deux des réponses à l'aide fcntl.flock pour suggèrent verrouiller le fichier shelve. Cependant, certaines des réponses dans ce fil , semblent indiquer que cela est lourd de problèmes - mais je ne sais pas exactement ce qu'ils sont. Il semble que cela se limite à Unix (mais apparemment Windows a un msvcrt.locking appelé équivalent), et le verrou est seulement « consultatif » - à savoir, il ne me empêchera pas d'écrire accidentellement le fichier sans le vérifier est verrouillé. Y at-il d'autres problèmes potentiels? Would écrit une copie du fichier, et le remplacement de la copie maîtresse comme une étape finale, réduire le risque de corruption?

Il ne semble pas que le DBM Module fera tout mieux que shelve. J'ai eu un coup d'œil à sqlite3 , mais il semble un peu exagéré pour ce objectif. Ce fil et celui-ci mentionne plusieurs bibliothèques 3ème partie, y compris

Était-ce utile?

La solution

sqlite3 hors de la boîte fournit ACIDE . Le verrouillage des fichiers est sujette à interraciales conditions et les problèmes de concurrence que vous n'aurez pas en utilisant sqlite3.

En fait, oui, sqlite3 est plus que ce dont vous avez besoin, mais ce n'est pas un fardeau énorme. Il peut fonctionner sur les téléphones mobiles, il est donc pas comme vous vous engagez à l'exécution d'un logiciel bestiale. Il va vous faire gagner du temps et le débogage des roues réinvente les problèmes de verrouillage.

Autres conseils

Je suppose que vous voulez continuer à memoize les résultats de la fonction dans la RAM, probablement dans un dictionnaire, mais utiliser la persistance de réduire le temps « warm-up » de l'application. Dans ce cas, vous n'allez pas être des éléments accéder au hasard directement dans le magasin de support pour une base de données pourrait en effet être surpuissant (même si, comme synthesizerpatel notes, peut-être pas autant que vous pensez).

Cependant, si vous voulez rouler votre propre, une stratégie viable pourrait être de charger simplement le dictionnaire à partir d'un fichier au début de votre course avant de commencer les discussions. Lorsqu'un résultat est pas dans le dictionnaire, alors vous devez écrire dans le fichier après l'avoir ajouté au dictionnaire. Vous pouvez le faire en l'ajoutant à une file d'attente et en utilisant un seul thread de travail qui vide des éléments de la file d'attente sur le disque (il suffit de les annexant à un seul fichier serait bien). Vous pouvez parfois ajouter le même résultat plus d'une fois, mais cela n'est pas fatale car ce sera le même résultat à chaque fois, donc en les lisant deux fois ou plus ne fera aucun mal réel. Le modèle de thread de Python vous garder hors de la plupart des types de problèmes (par exemple de la concurrence, annexant à une liste est atomique).

Voici un code (non testé, générique, incomplet) montre ce dont je parle:

import cPickle as pickle

import time, os.path

cache = {}
queue = []

# run at script start to warm up cache
def preload_cache(filename):
    if os.path.isfile(filename):
        with open(filename, "rb") as f:
            while True:
                try:
                    key, value = pickle.load(f), pickle.load(f)
                except EOFError:
                    break
                cache[key] = value

# your memoized function
def time_consuming_function(a, b, c, d):
    key = (a, b, c, d)
    if key in cache:
        return cache[key]
    else:
        # generate the result here
        # ...
        # add to cache, checking to see if it's already there again to avoid writing
        # it twice (in case another thread also added it) (this is not fatal, though)
        if key not in cache:
            cache[key] = result
            queue.append((key, result))
        return result

# run on worker thread to write new items out
def write_cache(filename):
    with open(filename, "ab") as f:
        while True:
            while queue:
                key, value = queue.pop()  # item order not important
                # but must write key and value in single call to ensure
                # both get written (otherwise, interrupting script might
                # leave only one written, corrupting the file)
                f.write(pickle.dumps(key, pickle.HIGHEST_PROTOCOL) +
                        pickle.dumps(value, pickle.HIGHEST_PROTOCOL))
            f.flush()
            time.sleep(1)

Si j'avais le temps, je voudrais en faire un décorateur ... et de mettre la persistance dans une sous-classe de dict ... l'utilisation des variables globales est également sous-optimale. :-) Si vous utilisez cette approche avec multiprocessing vous voudriez probablement utiliser un multiprocessing.Queue plutôt qu'une liste; vous pouvez alors utiliser queue.get() comme une attente de blocage pour un nouveau résultat dans le processus de travail qui écrit dans le fichier. Je n'ai pas utilisé multiprocessing, cependant, alors prenez ce peu de conseils avec un grain de sel.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top