Eu tenho uma tarefa que processa uma lista de arquivos no stdin. O tempo de inicialização do programa é substancial e a quantidade de tempo que cada arquivo leva varia muito. Quero gerar um número substancial desses processos e depois enviar o trabalho para os que não estiverem ocupados. Existem várias ferramentas diferentes de linha de comando que quase fazem o que eu quero, reduzi-o a duas opções quase funcionais:
find . -type f | split -n r/24 -u --filter="myjob"
find . -type f | parallel --pipe -u -l 1 myjob
O problema é que split
ele faz um round-robin puro, para que um dos processos fique para trás e fique para trás, atrasando a conclusão de toda a operação; enquanto parallel
quer gerar um processo por N linhas ou bytes de entrada e acabo gastando muito tempo na sobrecarga da inicialização.
Existe algo assim que reutilizará os processos e linhas de alimentação para quaisquer processos que tenham stdins desbloqueados?
fonte
split
comando? O nome entra em conflito com o utilitário de processamento de texto padrão .myjob
está pronta para receber mais informações. Não há como saber que um programa está pronto para processar mais entradas, tudo que você pode saber é que algum buffer em algum lugar (um buffer de pipe, um buffer de stdio) está pronto para receber mais entradas. Você pode providenciar para o seu programa enviar algum tipo de solicitação (por exemplo, exibir um prompt) quando estiver pronto?read
chamadas faria o truque. Esse é um empreendimento de programação bastante grande.-l 1
nosparallel
argumentos? IIRC, que diz paralelamente para processar uma linha de entrada por trabalho (ou seja, um nome de arquivo por bifurcação de myjob, com muita sobrecarga de inicialização).Respostas:
Isso não parece possível em um caso tão geral. Isso implica que você tem um buffer para cada processo e pode assistir os buffers de fora para decidir onde colocar a próxima entrada (programação) ... É claro que você pode escrever algo (ou usar um sistema em lote como slurm)
Mas, dependendo do processo, você poderá pré-processar a entrada. Por exemplo, se você deseja baixar arquivos, atualizar entradas de um banco de dados ou similar, mas 50% deles serão ignorados (e, portanto, você terá uma grande diferença de processamento dependendo da entrada), basta configurar um pré-processador que verifica quais entradas levarão muito tempo (arquivo existe, dados foram alterados etc.), para garantir que o que vier do outro lado leve um tempo bastante igual. Mesmo que a heurística não seja perfeita, você poderá obter uma melhoria considerável. Você pode despejar os outros em um arquivo e processar posteriormente da mesma maneira.
Mas isso depende do seu caso de uso.
fonte
Não, não há uma solução genérica. Seu despachante precisa saber quando cada programa está pronto para ler outra linha, e não há nenhum padrão que eu saiba que permita isso. Tudo o que você pode fazer é colocar uma linha em STDOUT e esperar que algo a consuma; não há realmente uma boa maneira de o produtor em um pipeline saber se o próximo consumidor está pronto ou não.
fonte
Acho que não. Na minha revista favorita, havia um artigo sobre programação de bash que fazia o que você queria. Estou disposto a acreditar que, se houvesse ferramentas para isso, eles as mencionariam. Então você quer algo parecido com:
Obviamente, você pode alterar a invocação para o script de trabalho real ao seu gosto. A revista que mencionei inicialmente faz coisas como configurar tubos e realmente iniciar threads de trabalho. Verifique
mkfifo
isso, mas essa rota é muito mais complicada, pois os processos de trabalho precisam sinalizar ao processo mestre que estão prontos para receber mais dados. Portanto, você precisa de um fifo para cada processo do trabalhador para enviar dados e um fifo para o processo mestre para receber informações dos trabalhadores.AVISO LEGAL Eu escrevi esse roteiro do alto da minha cabeça. Pode haver alguns problemas de sintaxe.
fonte
find . -type f | while read i
e nãofor i in $(find . -type f)
.Para o GNU Parallel, você pode definir o tamanho do bloco usando --block. No entanto, exige que você tenha memória suficiente para manter 1 bloco na memória para cada um dos processos em execução.
Entendo que não é exatamente isso que você está procurando, mas pode ser uma solução aceitável por enquanto.
Se, em média, suas tarefas durarem o mesmo tempo, você poderá usar o mbuffer:
fonte
Tente o seguinte:
mkfifo
para cada processo.Depois, aguarde
tail -f | myjob
cada quino.Por exemplo, configurando os trabalhadores (processos do meu emprego)
Dependendo do seu aplicativo (myjob), você poderá usar os trabalhos -s para encontrar trabalhos interrompidos. Caso contrário, liste os processos classificados por CPU e selecione o que consome menos recursos. De ter o relatório de trabalho em si, por exemplo, definindo um sinalizador no sistema de arquivos quando ele quer mais trabalho.
Supondo que o trabalho seja interrompido ao aguardar entrada, use
jobs -sl
descobrir pid de um trabalho interrompido e atribuí-lo a trabalho, por exemploEu testei isso com
Devo admitir que isso foi inventado de forma tão hummm.
fonte
O que é realmente necessário para resolver isso é um mecanismo de fila de algum tipo.
É possível que os trabalhos leiam suas entradas de uma Fila, como uma fila de mensagens SYSV, e os programas sejam executados em paralelo, basta enviar os valores para a fila?
Outra possibilidade é usar diretórios para a fila, assim:
pending
mv
dos primeiros arquivos que vê no diretório para um diretório irmão depending
, nomeadoinprogress
.pending
fonte
expondo a resposta do @ ash, você pode usar uma fila de mensagens SYSV para distribuir o trabalho. Se você não quiser escrever seu próprio programa em C, existe um utilitário chamado
ipcmd
que pode ajudar. Aqui está o que eu reuni para passar a saídafind $DIRECTORY -type f
para o$PARALLEL
número de processos:Aqui está um teste:
fonte
A menos que você possa estimar quanto tempo um arquivo de entrada específico será processado e os processos de trabalho não terão como relatar ao agendador (como fazem em cenários normais de computação paralela - geralmente por meio do MPI ), você geralmente não tem sorte. - pagar a penalidade de alguns trabalhadores processarem insumos por mais tempo do que outros (devido à desigualdade de insumos) ou pagar a penalidade de gerar um único processo novo para cada arquivo de insumos.
fonte
O GNU Parallel mudou nos últimos 7 anos. Então hoje ele pode fazer isso:
Este exemplo mostra que mais blocos são dados ao processo 11 e 10 do que ao processo 4 e 5 porque 4 e 5 são lidos mais lentamente:
fonte