Como evitar a computação cada vez que um módulo python é recarregado

StackOverflow https://stackoverflow.com/questions/195626

  •  10-07-2019
  •  | 
  •  

Pergunta

Eu tenho um módulo python que faz uso de um enorme dicionário variável global, atualmente eu colocar o código computação na parte superior, a cada primeira importação tempo ou recarga do módulo leva mais de um minuto, o que é totalmente inaceitável. Como posso guardar o resultado da computação em algum lugar para que a próxima importação / recarga não tem que calcular isso? Tentei cPickle, mas carregar a variável do dicionário de um arquivo (1.3M) leva aproximadamente o mesmo tempo que a computação.

Para dar mais informações sobre o meu problema,

FD = FreqDist(word for word in brown.words()) # this line of code takes 1 min
Foi útil?

Solução

Só para esclarecer: o código no corpo de um módulo é não executada toda vez que o módulo é importado - ele é executado apenas uma vez, após o qual as importações futuras encontrar o módulo já criado, em vez de recriá-lo. Dê uma olhada em sys.modules para ver a lista de módulos em cache.

No entanto, se o seu problema é o tempo que leva para a primeira importação depois que o programa é executado, você provavelmente precisará usar algum outro método que um dict python. Provavelmente o melhor seria a utilização de um formulário on-disco, por exemplo, um banco de dados SQLite, um dos módulos dbm.

Para uma mudança mínima na sua interface, o módulo de prateleira pode ser a sua melhor opção - Isso coloca interface bastante transparente entre os módulos dbm que os faz agir como um dict python arbitrário, permitindo que qualquer valor picklable para ser armazenado. Aqui está um exemplo:

# Create dict with a million items:
import shelve
d = shelve.open('path/to/my_persistant_dict')
d.update(('key%d' % x, x) for x in xrange(1000000))
d.close()

Em seguida, no próximo processo, usá-lo. Não deve haver grande atraso, como pesquisas só são realizadas para a chave solicitadas no formulário on-disco, por isso tudo não tem que se carregados na memória:

>>> d = shelve.open('path/to/my_persistant_dict')
>>> print d['key99999']
99999

É um pouco mais lento do que um dicionário real, e ainda levar um longo tempo para carregar, se você fizer algo que requer todas as chaves (por exemplo. Tente imprimi-lo), mas pode resolver o seu problema.

Outras dicas

Calcular o seu var global sobre o primeiro uso.

class Proxy:
    @property
    def global_name(self):
        # calculate your global var here, enable cache if needed
        ...

_proxy_object = Proxy()
GLOBAL_NAME = _proxy_object.global_name

Ou melhor ainda, os dados de acesso necessery via objeto de dados especial.

class Data:
    GLOBAL_NAME = property(...)

data = Data()

Exemplo:

from some_module import data

print(data.GLOBAL_NAME)

Django configurações .

Eu suponho que você colou o literal dict na fonte, e é isso que está tomando um minuto? Eu não sei como contornar isso, mas você provavelmente poderia evitar instanciar esse dict sobre import ... Você poderia preguiçosamente-instanciar a primeira vez que é realmente utilizada.

Você pode tentar usar a marechal módulo em vez do c ? Pickle um; poderia ser mais rápido. Este módulo é usado pelo Python para armazenar valores em um formato binário. Note-se especialmente o parágrafo seguinte, para ver se marshal se adapta às suas necessidades:

Nem todos os tipos de objetos Python são suportados; em geral, somente os objetos cujo valor é independente de uma invocação particular do Python podem ser escritos e lidos por este módulo. Os seguintes tipos são suportados: Nenhum, inteiros, inteiros longos, números de ponto, cordas flutuantes, objetos Unicode, tuplas, listas, conjuntos, dicionários e objetos de código, onde ele deve ser entendido que tuplas, listas e dicionários são suportados apenas contanto como os valores nele contidas são eles próprios suportado; e listas recursivas e dicionários não deve ser escrito (eles vão causar loops infinitos).

Só para ficar no lado seguro, antes unmarshalling o dict, certifique-se de que a versão Python que unmarshals o dict é o mesmo que aquele que fez o marechal, já que não há garantias para compatibilidade com versões anteriores.

Se a solução 'prateleira' acaba por ser muito lento ou complicadas, existem outras possibilidades:

shelve fica muito lento com grandes conjuntos de dados. Eu tenho usado Redis bastante com sucesso, e escreveu um FreqDist invólucro em torno dele. É muito rápido, e pode ser acessado simultaneamente.

Você pode usar um prateleira para loja seus dados no disco em vez de carregar os dados inteiros na memória. Assim, o tempo de inicialização será muito rápido, mas o trade-off será tempo de acesso mais lento.

Shelve vai pickle os valores de dicionários também, mas vai fazer o (des) não pickle na inicialização para todos os itens, mas apenas em tempo de acesso para cada item em si.

Um par de coisas que ajudará a acelerar as importações:

  1. Você pode tentar executar python usando a bandeira -OO ao executar python. Isso vai fazer algumas otimizações que reduzem o tempo de importação de módulos.
  2. Existe alguma razão para que você não poderia quebrar a até dicionário em dicionários menores em módulos separados que podem ser carregados mais rapidamente?
  3. Como último recurso, você poderia fazer os cálculos de forma assíncrona para que eles não vão atrasar o seu programa até que ele precisa os resultados. Ou talvez até mesmo colocar o dicionário em um processo separado e passar de volta dados e para trás usando IPC se você quer tirar proveito de arquiteturas multi-core.

Com o que disse, eu concordo que você não deve estar passando por qualquer atraso na importação de módulos depois da primeira vez de importá-lo. Aqui estão um par de outros pensamentos gerais:

  1. Você está importando o módulo dentro de uma função? Se assim for, este pode levar a problemas de desempenho uma vez que tem de verificar e ver se o módulo é carregado cada vez que ela atinge a declaração de importação.
  2. O seu programa multi-threaded? Eu vi ocasiões em que a execução de código em cima módulo de importação em um aplicativo de multi-threaded pode causar alguns wonkiness e aplicação instabilidade (principalmente com o módulo cgitb).
  3. Se esta é uma variável global, estar ciente de que os tempos variável global de pesquisa pode ser significativamente maior do que os tempos de pesquisa variável locais. Neste caso, você pode conseguir uma melhoria significativa no desempenho ligando o dicionário para uma variável local se você estiver usando-o várias vezes no mesmo contexto.

Com isso dito, é um pouco pouco difícil de dar-lhe algum conselho específico sem um pouco mais contexto. Mais especificamente, onde você importá-lo? E quais são os cálculos?

  1. o Factor a parte computacionalmente intensiva em um módulo separado. Então, pelo menos na recarga, você não vai ter que esperar.

  2. Tente despejar a estrutura de dados usando o protocolo 2. O comando para tentar seria cPickle.dump(FD, protocol=2). Desde o docstring para cPickle.Pickler:

    Protocol 0 is the
    only protocol that can be written to a file opened in text
    mode and read back successfully.  When using a protocol higher
    than 0, make sure the file is opened in binary mode, both when
    pickling and unpickling. 
    

Eu estou passando por esse mesmo problema ... prateleira, bancos de dados, etc ... são todos muito lento para este tipo de problema. Você precisará tomar a batida uma vez, inseri-lo em uma chave loja InMemory / val como Redis. Ela só vai morar lá na memória (advertindo que poderia usar-se uma boa quantidade de memória para que você pode querer uma caixa dedicada). Você nunca vai ter que carregá-lo e você vai apenas ficar olhando na memória para as teclas

r = Redis()
r.set(key, word)

word = r.get(key)

Expandindo a ideia-cálculo atrasou, por que não transformar o dict em uma classe que suprimentos (e caches) como elementos necessários?

Você também pode usar psyco para acelerar a execução global ...

ou você pode simplesmente usar um banco de dados para armazenar os valores? Confira SQLObject, o que torna muito fácil de coisas loja para um banco de dados.

Há uma outra solução muito óbvia para este problema. Quando o código é recarregado o escopo original ainda está disponível.

Então ... fazer algo como isto irá certificar-se esse código é executado apenas uma vez.

try:
    FD
except NameError:
    FD = FreqDist(word for word in brown.words())
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top