Objetos de memória compartilhada no multiprocessamento
Suponha que eu tenha uma matriz numpy grande de memória, que tenha uma função funcque aceite essa matriz gigante como entrada (junto com alguns outros parâmetros). funccom parâmetros diferentes pode ser executado em paralelo. Por exemplo:
Se eu usar a biblioteca de multiprocessamento, essa matriz gigante será copiada várias vezes em diferentes processos.
Existe uma maneira de permitir que diferentes processos compartilhem a mesma matriz? Este objeto de matriz é somente leitura e nunca será modificado.
O que é mais complicado, se arr não é uma matriz, mas um objeto python arbitrário, existe uma maneira de compartilhá-la?
Eu li a resposta, mas ainda estou um pouco confuso. Como fork () é copiado na gravação, não devemos invocar nenhum custo adicional ao gerar novos processos na biblioteca de multiprocessamento python. Mas o código a seguir sugere uma enorme sobrecarga:
saída (e, a propósito, o custo aumenta à medida que o tamanho da matriz aumenta, então eu suspeito que ainda haja sobrecarga relacionada à cópia de memória):
Por que existe uma sobrecarga tão grande, se não copiamos a matriz? E que parte a memória compartilhada me salva?
possível duplicata de Os dados compartilhados somente leitura são copiados para diferentes processos para o multiprocessamento Python?
Você olhou para os documentos , certo?
@FrancisAvila existe uma maneira de compartilhar não apenas array, mas objetos python arbitrários?
@LevLevitsky eu tenho que perguntar, existe uma maneira de compartilhar não apenas array, mas objetos python arbitrários?
Esta resposta explica bem por que objetos Python arbitrários não podem ser compartilhados.
Se você usar um sistema operacional que usa fork()semântica de cópia na gravação (como qualquer unix comum), desde que você nunca altere sua estrutura de dados, ela estará disponível para todos os processos filhos sem ocupar memória adicional. Você não precisará fazer nada de especial (exceto tenha certeza absoluta de não alterar o objeto).
A coisa mais eficiente que você pode fazer pelo seu problema seria compactar sua matriz em uma estrutura de matriz eficiente (usando numpyou array), coloque-a na memória compartilhada, envolva-a multiprocessing.Arraye passe-a para suas funções. Esta resposta mostra como fazer isso .
Se você deseja um objeto compartilhado gravável , precisará envolvê-lo com algum tipo de sincronização ou bloqueio. multiprocessingfornece dois métodos para fazer isso : um usando memória compartilhada (adequado para valores simples, matrizes ou ctypes) ou umManager proxy, em que um processo retém a memória e um gerente arbitra o acesso a ela de outros processos (mesmo em uma rede).
o Manager abordagem pode ser usada com objetos Python arbitrários, mas será mais lenta que o equivalente usando memória compartilhada porque os objetos precisam ser serializados / desserializados e enviados entre processos.
Existem inúmeras bibliotecas e abordagens de processamento paralelo disponíveis no Python . multiprocessingé uma biblioteca excelente e bem arredondada, mas se você tiver necessidades especiais, talvez uma das outras abordagens seja melhor.
Só para observar, no fork do Python (), na verdade, significa cópia no acesso (porque apenas o acesso ao objeto alterará sua contagem de ref).
@FabioZadrozny Copiaria realmente o objeto inteiro ou apenas a página de memória que contém sua refcount?
AFAIK, apenas a página de memória que contém o refcount (portanto, 4kb em cada acesso ao objeto).
@max Use um fechamento. A função atribuída apply_asyncdeve fazer referência ao objeto compartilhado no escopo diretamente, e não através de seus argumentos.
@FrancisAvila como você usa um fechamento? A função que você atribui ao apply_async não deve ser selecionável? Ou isso é apenas uma restrição map_async?
— GermanK
Encontrei o mesmo problema e escrevi uma pequena classe de utilitário de memória compartilhada para contornar isso.
Estou usando multiprocessing.RawArray(sem bloqueio), e também o acesso às matrizes não é sincronizado (sem bloqueio), tenha cuidado para não atirar nos próprios pés.
Com a solução, recebo acelerações por um fator de aproximadamente 3 em um i7 quad-core.
Aqui está o código: sinta-se à vontade para usá-lo e melhorá-lo, e informe quaisquer bugs.
— martin.preinfalk
Acabei de perceber que você precisa configurar suas matrizes de memória compartilhada antes de criar o pool de multiprocessamento, ainda não sei o porquê, mas definitivamente não funcionará ao contrário.
o motivo é que o pool de multiprocessamento chama fork () quando o pool é instanciado; portanto, nada depois disso terá acesso ao ponteiro para qualquer mem compartilhado criado posteriormente.
Quando tentei esse código no py35, recebi uma exceção no multiprocessing.sharedctypes.py, então acho que esse código é apenas para o py2.
— Dr. Hillier Dániel
Este é o caso de uso pretendido para Ray , que é uma biblioteca para Python paralelo e distribuído. Sob o capô, ele serializa objetos usando o layout de dados Apache Arrow (que é um formato de cópia zero) e os armazena em um armazenamento de objetos de memória compartilhada para que possam ser acessados ​​por vários processos sem criar cópias.
O código seria semelhante ao seguinte.
Se você não ligar ray.put, o array ainda será armazenado na memória compartilhada, mas isso será feito uma vez por chamada func, o que não é o que você deseja.
Observe que isso funcionará não apenas para matrizes, mas também para objetos que contêm matrizes , por exemplo, dicionários que mapeiam entradas para matrizes como abaixo.
Você pode comparar o desempenho da serialização no Ray versus pickle executando o seguinte no IPython.
A serialização com Ray é apenas um pouco mais rápida que a pickle, mas a desserialização é 1000x mais rápida por causa do uso de memória compartilhada (esse número, é claro, depende do objeto).
Veja a documentação do Ray . Você pode ler mais sobre serialização rápida usando Ray e Arrow . Note que eu sou um dos desenvolvedores Ray.
— Robert Nishihara
Ray parece bom! Mas, eu tentei usar essa biblioteca antes, mas infelizmente percebi que Ray não suporta janelas. Espero que vocês possam suportar o Windows o mais rápido possível. Obrigado, desenvolvedores!
Como Robert Nishihara mencionou, o Apache Arrow facilita isso, especificamente com o armazenamento de objetos na memória do Plasma, no qual Ray é construído.
Fiz cérebro de plasma especificamente para esta razão - carregamento rápido e recarga de grandes objetos em um aplicativo Flask. É um namespace de objeto de memória compartilhada para objetos serializáveis ​​do Apache Arrow, incluindo pickle'd bytestrings geradas por pickle.dumps(...).
A principal diferença com o Apache Ray e o Plasma é que ele controla os IDs de objetos para você. Quaisquer processos, threads ou programas em execução localmente podem compartilhar os valores das variáveis ​​chamando o nome de qualquer Brainobjeto.