Pergunta

EDIT: Eu tenho marcado esse C em uma esperança de obter mais resposta. É mais a teoria Estou interessado em que uma implementação da linguagem específica. Então, se você é um C codificador por favor, trate o seguinte PHP como pseudo-código e se sentir livre para responder com uma resposta por escrito em C.

Eu estou tentando acelerar um script PHP CLI por tê-lo executar suas tarefas em paralelo em vez de em série. As tarefas são completamente independentes um do outro, por isso não importa que ordem eles começam / acabamento em.

Aqui está o roteiro original (observe todos estes exemplos são retirados-back para maior clareza):

<?php

$items = range(0, 100);

function do_stuff_with($item) { echo "$item\n"; }

foreach ($items as $item) {
    do_stuff_with($item);
}

Eu consegui fazê-lo funcionar no $items em paralelo com pcntl_fork() como mostrado abaixo:

<?php

ini_set('max_execution_time', 0); 
ini_set('max_input_time', 0); 
set_time_limit(0);

$items = range(0, 100);

function do_stuff_with($item) { echo "$item\n"; }

$pids = array();
foreach ($items as $item) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        die("couldn't fork()");
    } elseif ($pid > 0) {
        // parent
        $pids[] = $pid;
    } else {
        // child
        do_stuff_with($item);
        exit(0);
    }   
}

foreach ($pids as $pid) {
    pcntl_waitpid($pid, $status);
}

Agora eu quero estender esse então não há um máximo de, digamos, 10 crianças ativas ao mesmo tempo. Qual é a melhor maneira de lidar com isso? Eu tentei algumas coisas, mas não tive muita sorte.

Foi útil?

Solução

Não há syscall para obter uma lista de pids criança, mas ps pode fazer isso por você.

interruptor --ppid irá listar todas as crianças para você processar assim você só precisa contar o número de linhas produzidos pela ps.

Como alternativa, você pode manter o seu próprio contador que você vai incrementar em fork() e decréscimo no sinal SIGCHLD, assumindo estadias ppid inalterada durante fork'ed processado.

Outras dicas

A melhor coisa que eu posso vir acima com é adicionar todas as tarefas para uma fila, lançar o número máximo de segmentos que você quer, e depois ter cada thread solicitando uma tarefa da fila, executar a tarefa e solicitando o próximo . Não se esqueça de ter os fios termina quando não houver mais tarefas para fazer.

bifurcação é uma operação cara. Desde a aparência dele, o que você realmente quer é multi rosqueamento , e não vários processamento . A diferença é que tópicos são peso muito mais leve do que os processos, desde tópicos compartilhar um espaço de endereço virtual, mas os processos têm espaços de endereços virtuais separadas.

Eu não sou um desenvolvedor PHP, mas uma rápida pesquisa no Google revela que o PHP não suporta multithreading nativamente, mas existem bibliotecas para fazer o trabalho.

De qualquer forma, uma vez que você descobrir como tópicos de desova, você deve descobrir quantos threads para desovar. A fim de fazer isso, você precisa saber o que o gargalo de sua aplicação é. É a CPU gargalo, memória ou I / O? Você indicou em seus comentários que são vinculados à rede, ea rede é um tipo de I / O.

Se você estava vinculada à CPU, você só vai conseguir o máximo de paralelismo como você tem núcleos de CPU; qualquer mais threads e você está apenas perdendo tempo fazendo trocas de contexto. Assumindo que você pode descobrir quantos total de tópicos para desovar, você deve dividir seu trabalho em que muitas unidades, e que cada unidade de um processo de discussão de forma independente.

Se você estava de memória associados, então multithreading não iria ajuda.

Uma vez que você está I / O ligado, descobrir quantos threads para desova é um pouco mais complicado. Se todos os itens de trabalho ter aproximadamente o mesmo tempo para processo com muito baixa variância, você pode estimar quantos threads para desovar medindo quanto tempo um item de trabalho leva. No entanto, uma vez que pacotes de rede tendem a ter latências altamente variáveis, isso é pouco provável que seja o caso.

Uma opção é usar pools de threads - você cria um monte de tópicos e, em seguida, para cada item de processo, você ver se há um fio livre na piscina. Se houver, você tem esse segmento executar o trabalho, e você passar para o próximo item. Caso contrário, você esperar por um fio para se tornar disponível. Escolhendo o tamanho do pool de threads é importante - muito grande, e você está desperdiçando tempo fazendo trocas de contexto desnecessárias. Muito poucos, e você está esperando por tópicos muitas vezes.

No entanto, outra opção é abandonar multithreading / multiprocessamento e apenas fazer assíncrono I / O em seu lugar. Desde que você mencionou que você está trabalhando em um processador single-core, este será, provavelmente, a opção mais rápida. Você pode usar funções como socket_select() para testar se um soquete tem dados disponíveis. Se isso acontecer, você pode ler os dados, caso contrário, você passar para uma tomada diferente. Isso requer fazendo muito mais contabilidade, mas você evita à espera de dados para entrar em uma tomada quando está disponível em uma tomada diferente de dados.

Se você quiser evitar tópicos e / S assíncrona e ficar com multiprocessamento, ele ainda pode ser útil se o processamento por item é caro o suficiente. Você pode então fazer a divisão do trabalho assim:

$my_process_index = 0;
$pids = array();

// Fork off $max_procs processes
for($i = 0; $i < $max_procs - 1; $i++)
{
  $pid = pcntl_fork();
  if($pid == -1)
  {
    die("couldn't fork()");
  }
  elseif($pid > 0)
  {
    // parent
    $my_process_index++;
    $pids[] = $pid
  }
  else
  {
    // child
    break;
  }
}

// $my_process_index is now an integer in the range [0, $max_procs), unique among all the processes
// Each process will now process 1/$max_procs of the items
for($i = $my_process_index; $i < length($items); $i += $max_procs)
{
  do_stuff_with($items[$i]);
}

if($my_process_index != 0)
{
  exit(0);
}

man 2 setrlimit

Isso vai ser por usuário que pode ser o que você quer de qualquer maneira.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top