Как я могу обеспечить максимальное количество разветвленных детей?

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

Вопрос

РЕДАКТИРОВАТЬ: я отметил этот C в надежде получить больше ответа. Меня интересует больше теория, чем конкретная языковая реализация. Поэтому, если вы кодер на C, пожалуйста, рассматривайте следующий PHP как псевдокод и не стесняйтесь отвечать ответом, написанным на C.

Я пытаюсь ускорить выполнение сценария PHP CLI, выполняя его задачи параллельно, а не последовательно. Задачи полностью независимы друг от друга, поэтому не имеет значения, в каком порядке они начинаются / заканчиваются.

Вот оригинальный сценарий (для ясности обратите внимание на все эти примеры):

<?php

$items = range(0, 100);

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

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

Мне удалось заставить его работать над $ items параллельно с pcntl_fork () , как показано ниже:

<?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);
}

Теперь я хочу расширить это, чтобы максимум, скажем, 10 детей были активными одновременно. Какой лучший способ справиться с этим? Я попробовал несколько вещей, но мне не повезло.

Это было полезно?

Решение

Нет системного вызова для получения списка дочерних pids, но ps может сделать это за вас.

Переключатель

- ppid выведет список всех дочерних элементов для вашего процесса, поэтому вам просто нужно подсчитать количество строк, выданных ps .

В качестве альтернативы вы можете сохранить свой собственный счетчик, который вы будете увеличивать для fork () и уменьшать для сигнала SIGCHLD , предполагая, что ppid остается неизменным для обработано форк.

Другие советы

Лучшее, что я могу придумать, - это добавить все задачи в очередь, запустить максимальное количество потоков, которое вы хотите, и затем каждый поток запрашивает задачу из очереди, выполняет задачу и запрашивает следующую. , Не забывайте прерывать потоки, когда больше нет задач.

Форкинг - дорогостоящая операция. Судя по всему, вам действительно нужна многопоточная многопоточность , а не многопользовательская обработка . Разница в том, что потоки имеют гораздо меньший вес, чем процессы, поскольку потоки используют виртуальное адресное пространство, а процессы имеют отдельные виртуальные адресные пространства.

Я не разработчик PHP, но быстрый поиск в Google показывает, что PHP изначально не поддерживает многопоточность, но есть библиотеки для этой работы.

В любом случае, когда вы поймете, как создавать потоки, вы должны выяснить, сколько потоков создавать. Для этого вам нужно знать, в чем заключается узкое место вашего приложения. Является ли узким местом процессор, память или ввод-вывод? Вы указали в своих комментариях, что вы привязаны к сети, а сеть - это тип ввода / вывода.

Если бы вы были привязаны к процессору, вы получите столько параллелизма, сколько у вас процессорных ядер; больше потоков, и вы просто тратите время на переключение контекста. Предполагая, что вы можете выяснить, сколько всего потоков должно появиться, вы должны разделить свою работу на это количество модулей и сделать так, чтобы каждый поток обрабатывал один модуль независимо.

Если бы вы были связаны с памятью, многопоточность не помогла бы.

Поскольку вы привязаны к вводу / выводу, выяснить, сколько потоков для порождения, немного сложнее. Если на обработку всех рабочих элементов требуется примерно одно и то же время с очень малой дисперсией, вы можете оценить, сколько потоков порождать, измерив, сколько времени занимает один рабочий элемент. Однако, поскольку сетевые пакеты, как правило, имеют очень переменную задержку, это вряд ли имеет место.

Одним из вариантов является использование пулов потоков - вы создаете целую кучу потоков, а затем для каждого обрабатываемого элемента вы видите, есть ли в пуле свободная нить. Если есть, этот поток выполняет эту работу, и вы переходите к следующему элементу. В противном случае вы ждете, когда поток станет доступным. Выбор размера пула потоков важен - слишком велик, и вы тратите время на ненужные переключения контекста. Слишком мало, и вы слишком часто ждете потоков.

Еще один вариант - отказаться от многопоточности / многопроцессорности и просто выполнить вместо этого асинхронный ввод-вывод. Поскольку вы упомянули, что работаете над одноядерным процессором, это, вероятно, самый быстрый вариант. Вы можете использовать такие функции, как socket_select () проверить, есть ли в сокете доступные данные. Если это так, вы можете прочитать данные, в противном случае вы переходите на другой сокет. Это требует гораздо большего учета, но вы избегаете ожидания поступления данных в один сокет, когда данные доступны в другом сокете.

Если вы хотите отказаться от потоков и асинхронного ввода-вывода и придерживаться многопроцессорной обработки, все равно может оказаться целесообразным, если обработка каждого элемента будет достаточно дорогой. Затем вы можете выполнить разделение работы следующим образом:

$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

Это будет для каждого пользователя, который может быть тем, что вы хотите в любом случае.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top