Os processos filhos gerados por meio de multiprocessamento compartilham objetos criados anteriormente no programa?
Eu tenho a seguinte configuração:
do_some_processing(filename):
for line in file(filename):
if line.split(',')[0] in big_lookup_object:
# something here
if __name__ == '__main__':
big_lookup_object = marshal.load('file.bin')
pool = Pool(processes=4)
print pool.map(do_some_processing, glob.glob('*.data'))
Estou carregando um grande objeto na memória e, em seguida, criando um pool de trabalhadores que precisam fazer uso desse grande objeto. O objeto grande é acessado somente leitura, não preciso passar modificações dele entre os processos.
Minha pergunta é: o objeto grande está carregado na memória compartilhada, como seria se eu gerasse um processo em unix / c, ou cada processo carrega sua própria cópia do objeto grande?
Atualização: para esclarecer mais - big_lookup_object é um objeto de pesquisa compartilhado. Eu não preciso dividir isso e processá-lo separadamente. Preciso manter uma única cópia dele. O trabalho que preciso dividir é ler muitos outros arquivos grandes e comparar os itens nesses arquivos grandes com o objeto de pesquisa.
Atualização adicional: o banco de dados é uma boa solução, o memcached pode ser uma solução melhor e o arquivo em disco (arquivar ou dbm) pode ser ainda melhor. Nesta questão, eu estava particularmente interessado em uma solução em memória. Para a solução final, estarei usando o hadoop, mas gostaria de ver se também posso ter uma versão na memória local.
fonte
marshal.load
para pai e para cada filho (cada processo importa o módulo).Respostas:
"Os processos filhos gerados por meio de multiprocessamento compartilham objetos criados anteriormente no programa?"
Não (python antes de 3.8) e Sim em 3.8 ( https://docs.python.org/3/library/multiprocessing.shared_memory.html#module-multiprocessing.shared_memory )
Os processos têm espaço de memória independente.
Solução 1
Para fazer o melhor uso de uma grande estrutura com muitos trabalhadores, faça isso.
Escreva cada trabalhador como um "filtro" - lê os resultados intermediários do stdin, funciona, escreve os resultados intermediários no stdout.
Conecte todos os trabalhadores como um pipeline:
Cada processo lê, funciona e grava.
Isso é extremamente eficiente, pois todos os processos estão sendo executados simultaneamente. As gravações e leituras passam diretamente por buffers compartilhados entre os processos.
Solução 2
Em alguns casos, você tem uma estrutura mais complexa - geralmente uma estrutura "fan-out". Neste caso, você tem um pai com vários filhos.
Pai abre dados de origem. O pai bifurca vários filhos.
O pai lê a fonte, distribui partes da fonte para cada filho em execução simultaneamente.
Quando o pai chegar ao fim, feche o cano. O filho obtém o fim do arquivo e termina normalmente.
As partes infantis são agradáveis de escrever porque cada criança simplesmente lê
sys.stdin
.O pai tem um pouco de trabalho de pés elaborado para desovar todos os filhos e reter os canos de maneira adequada, mas não é tão ruim.
Fan-in é a estrutura oposta. Vários processos em execução independente precisam intercalar suas entradas em um processo comum. O colecionador não é tão fácil de escrever, pois tem que ler de várias fontes.
A leitura de muitos canais nomeados geralmente é feita usando o
select
módulo para ver quais canais têm entrada pendente.Solução 3
A pesquisa compartilhada é a definição de um banco de dados.
Solução 3A - carregue um banco de dados. Deixe os trabalhadores processarem os dados no banco de dados.
Solução 3B - crie um servidor muito simples usando werkzeug (ou similar) para fornecer aplicativos WSGI que respondem a HTTP GET para que os trabalhadores possam consultar o servidor.
Solução 4
Objeto do sistema de arquivos compartilhado. O sistema operacional Unix oferece objetos de memória compartilhada. Esses são apenas arquivos mapeados para a memória para que a troca de E / S seja feita em vez de mais leituras com buffer de convenção.
Você pode fazer isso a partir de um contexto Python de várias maneiras
Escreva um programa de inicialização que (1) divida seu objeto gigante original em objetos menores e (2) inicie trabalhadores, cada um com um objeto menor. Os objetos menores podem ser objetos Python em conserva para economizar um pouquinho de tempo de leitura de arquivo.
Escreva um programa de inicialização que (1) leia seu gigantesco objeto original e escreva um arquivo estruturado em página e codificado em bytes usando
seek
operações para garantir que seções individuais sejam fáceis de encontrar com buscas simples. Isso é o que um mecanismo de banco de dados faz - divide os dados em páginas, torna cada página fácil de localizar por meio de umseek
.Gerar trabalhadores com acesso a este grande arquivo estruturado por página. Cada trabalhador pode procurar as partes relevantes e aí fazer o seu trabalho.
fonte
Os processos filhos gerados por meio de multiprocessamento compartilham objetos criados anteriormente no programa?
Depende. Para variáveis globais somente leitura, muitas vezes isso pode ser considerado (além da memória consumida), caso contrário, não deveria.
a documentação do multiprocessamento diz:
Better to inherit than pickle/unpickle
Explicitly pass resources to child processes
Global variables
Exemplo
No Windows (CPU única):
Com
sleep
:Sem
sleep
:fonte
z
não é compartilhado. Isso responde à pergunta com: "não, pelo menos no Windows, uma variável pai não é compartilhada entre os filhos".z
caso), eles podem ser considerados compartilhados.S.Lott está correto. Os atalhos de multiprocessamento do Python fornecem efetivamente um bloco de memória duplicado separado.
Na maioria dos sistemas * nix, usar uma chamada de nível inferior para
os.fork()
fornecerá, de fato, memória de cópia na gravação, que pode ser o que você está pensando. AFAIK, em teoria, no mais simplista dos programas possível, você poderia ler esses dados sem tê-los duplicados.No entanto, as coisas não são tão simples no interpretador Python. Os dados e metadados do objeto são armazenados no mesmo segmento de memória, portanto, mesmo que o objeto nunca mude, algo como um contador de referência para aquele objeto que está sendo incrementado causará uma gravação na memória e, portanto, uma cópia. Quase qualquer programa Python que está fazendo mais do que "imprimir 'olá'" causará incrementos na contagem de referência, então você provavelmente nunca perceberá o benefício da cópia na gravação.
Mesmo se alguém conseguisse hackear uma solução de memória compartilhada em Python, tentar coordenar a coleta de lixo entre os processos provavelmente seria muito doloroso.
fonte
Se você estiver executando no Unix, eles podem compartilhar o mesmo objeto, devido à forma como o fork funciona (ou seja, os processos filhos têm memória separada, mas é cópia-na-gravação, então pode ser compartilhado desde que ninguém o modifique). Tentei o seguinte:
e obteve o seguinte resultado:
É claro que isso não prova que uma cópia não foi feita, mas você deve ser capaz de verificar isso em sua situação olhando a saída de
ps
para ver quanta memória real cada subprocesso está usando.fonte
Processos diferentes têm espaços de endereço diferentes. Como executar diferentes instâncias do interpretador. É para isso que serve o IPC (comunicação entre processos).
Você pode usar filas ou canais para essa finalidade. Você também pode usar rpc sobre tcp se quiser distribuir os processos por uma rede posteriormente.
http://docs.python.org/dev/library/multiprocessing.html#exchanging-objects-between-processes
fonte
Não está diretamente relacionado ao multiprocessamento em si, mas pelo seu exemplo, parece que você poderia usar apenas o módulo shelve ou algo parecido. O "big_lookup_object" realmente precisa estar completamente na memória?
fonte
Não, mas você pode carregar seus dados como um processo filho e permitir que ele compartilhe seus dados com outros filhos. ver abaixo.
fonte
Para a plataforma Linux / Unix / MacOS, forkmap é uma solução rápida e suja.
fonte