Параллелим процесс на PHP

Давно уже наткнулся на одну статью, автор которой приводит пример распараллеливания работы php-скриптов через сокеты, но некогда было ее прочитать. Ссылку на эту статью можете увидеть в конце поста. Вот, наконец, добрался до нее и после прочтения возникло дикое желание распараллелить всем известный пример вычисления числа PI на MPI. У меня двуядерная машинка, поэтому эффект от распараллеливания должен проявиться.

В итоге, переработав пример из статьи про распараллеливание, получилось два скрипта – test.php и pi.php. Первый скрипт является главным и управляет работой второго. Второй скрипт отвечает за вычисление числа PI, т.е. именно он запускается параллельно.

test.php

<?
// функция запуска параллельной задачи
function StartTask($server, $uri, $post) {
	$fp = fsockopen($server, 80);
	// формируем переменные для передачи скрипту методом POST
	$_post = Array();
	if (is_array($post)) {
		foreach ($post as $name => $value) {
			$_post[] = $name.'='.urlencode($value);
		}
	}
	$post = implode('&', $_post);
	stream_set_blocking($fp, false);
	stream_set_timeout($fp, 86400);
	// вызываем скрипт, передавая ему необходимые переменные
	fwrite($fp,
			"POST /$uri HTTP/1.1\r\nHost: $server \r\n".
			"Content-Type: application/x-www-form-urlencoded\r\n".
			"Content-Length: ".strlen($post)."\r\n".
			"Connection: close\r\n\r\n$post"
	);
	return $fp;
}
// эта функция берет вывод параллельного скрипта и возвращает его
function GetTaskOutput(&$fp) {
	if ($fp === false) {
		return false;
	}
	if (feof($fp)) {
		fclose($fp);
		$fp = false;
		return false;
	}
	return fread($fp, 512);
}
// функция для вычисления времени (чтобы замерить время выполнения кода)
function GetMTime() {
	$mtime = microtime();
	$mtime = explode(' ', $mtime);
	$mtime = $mtime[1] + $mtime[0];
	return $mtime;
}
set_time_limit(0); // это обязательно должно быть нулем, иначе настройки php.ini могут не позволить скрипту работать долго
// Запускаем сначала для одного потока 5 экспериментов, потом - для двух потоков. И посмотрим, какое будет время.
$n = 40000000; // кол-во интервалов
$PI25DT = 3.141592653589793238462643; // точное значение числа PI
for ($size = 1; $size <= 2; $size++) { // кол-во процессов всего
	$minTime = 0;
	for ($k = 0; $k < 5; $k++) {
		$time1 = GetMTime(); // засекаем время
		$fp = Array();
		for ($i = 0; $i < $size; $i++) { // стартуем скрипты
			$fp[$i] = StartTask('test','/pi.php', Array('rank' => $i, 'size' => $size, 'n' => $n));
		}
		$mypi = 0;
		while (true) {
			$out = Array();
			$break = true;
			for ($i = 0; $i < $size; $i++) {
				$out[$i] = GetTaskOutput($fp[$i]);
				if ($out[$i] !== false) {
					$break = false;
				}
			}
			if ($break) {
				break;
			}
			// выделяем строку результата из вывода скрипта. Почему так? Попробуйте без этого и будет понятно
			for ($i = 0; $i < $size; $i++) {
				if (preg_match('!<result>(.*)?</result>!is', $out[$i], $matches) > 0) {
					$mypi += $matches[1];
				}
			}
		}
		$time2 = GetMTime();
		if ($time2 - $time1 < $minTime || $k == 0) {
			$minTime = $time2 - $time1;
		}
	}
	echo '<pre>';
	echo 'Size: '.$size.'<br />'; // выводим кол-во скриптов, параллельно работавших
	echo 'N: '.$n.'<br />';
	echo 'Exact PI = '.number_format($PI25DT, 16).'<br />';
	echo 'My PI    = '.number_format($mypi, 16).'<br />'; // выводим вычисленное значение числа PI
	echo 'Time, sec: '.$minTime; // минимальное время работы в течение всех экспериментов
	echo '</pre>';
	flush(); @ob_flush();
}
?>

pi.php

<?
set_time_limit(0);
$rank = $_REQUEST['rank']; // номер процесса
$size = $_REQUEST['size']; // кол-во процессов всего
$n    = $_REQUEST['n'];    // кол-во интервалов
$h   = 1.0 / $n;
$sum = 0.0;
for ($i = $rank + 1; $i <= $n; $i += $size) {
	$x    = $h * ($i - 0.5);
	$sum += (4.0 / (1.0 + $x * $x));
}
$mypi = $h * $sum; 
echo '<result>'.$mypi.'</result>';
?>

Я не буду говорить о методах вычисления числа PI в этом посте, т.к. это тема отдельного разговора. Я просто посмотрел на реализацию параллельного алгоритма вычисления PI в примерах библиотеки MPI и сделал то же самое, но на PHP.

Да, тут в примере я провожу сначала 5 экспериментов для одного процесса, потом те же 5 экспериментов, но для двух параллельных процессов. Т.е. в сумме 10 экспериментов – 2 итерации по 5 итераций в каждой.

Тут конечно самое интересное – а действительно ли будет выигрыш от распараллеливания? Я решил проверить, и вот что у меня получилось:

Size: 1
N: 40000000
Exact PI = 3.1415926535897931
My PI = 3.1415926535888001
Time, sec: 22.361926078796

Size: 2
N: 40000000
Exact PI = 3.1415926535897931
My PI = 3.1415926535901999
Time, sec: 15.371038913727

Первый результат – для 40 миллионов интервалов и одного процесса. Второй результат – то же самое, но уже распараллелено на два процесса. Как видим, погрешность во втором случае чуть меньше, чем в первом. Но самое интересное – выигрыш есть, и он оказался 30-ти процентным. Неплохой результат однако :) В идеале конечно должно быть 50%, но такого не бывает никогда, т.к. очень много посторонних факторов влияет на процессы и процессоры.

Почему я брал минимальное время после проведения каждых 5 экспериментов? Почему не среднее? Дело в том, что так правильнее :) Минимальное время показывает, что на такой итерации с таким временем процессор был меньше загружен всяким мусором (работающими на фоне программами и т.п.). А если бы мы вычисляли среднее время – мы бы брали в расчет и мусор, который нескромно влияет на результат.

Я, конечно, мог бы делать не 5 итераций, а, скажем, 50, но тогда пришлось бы ждать более получаса, чтобы процессы отработали :) Если кому интересно – можете и на сотне итераций пробовать :))

Чем больше итераций, тем точнее результат.

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

Вычисление PI – это пример, первый пришедший в голову, и сразу пошедший в реализацию :) На самом деле можно много чего придумать. Приведенный выше код можно подточить под собственные нужды.

А вот и сама ссылка на первоисточник: [ссылка]





Читайте также:



13 Ответов на “Параллелим процесс на PHP”

  1. Anton

    спс, интересно.

    Я считаю надо все-таки среднее время брать, поскольку мы же не знаем, насколько влияние “мусора” отличается в паралельном и непаралельном случаях (для минимальных результатов), а разница во влияниях “мусора” может быть довольно большой (проверено на деле:)).
    А так мы усредняем это влияние… довольно спорный вопрос:)

  2. О вреде многозадачности применительно к людям
    [ссылка]

  3. novice

    Спасибо, adword! Очень интересная и правильная статья! Советую всем прочитать.

  4. Рекомендую к прочтению все труды Джоэл Спольски!
    [ссылка]

  5. warp

    Неплохо, согласен.
    Но тоже считаю, что за результат надо брать среднее время выполнения, ибо:
    1) если считать загрузку ЦП фоновыми программами чем то вроде некого шума, смезывающего итоговую картину, то надо наоборот стремиться уровнять его значение для обоих случаев за счет увеличения числа итераций и нахождения среднего арифметического, или же пытаться исключить вовсе (что в нашем случае почти нереально);
    2) есть значительный риск получить крайне неточные результаты в столь малом количестве итераций (если в конкретный момент времени мусора в ЦП окажется меньше, чем обычно);

  6. TomDeLonge

    Хотелось бы увидеть еще что то о многопоточности в php, в частности о мульти кУРЛ)

  7. Igor

    Интересная и полезная статья.

  8. Интересно… Спасибо)

  9. borzovich

    Спасибо.Сам не догадался о такой организации мультипоточности :)

  10. А почему нельзя использовать банальные pthreads?)

  11. novice

    Иногда хочется чего-нибудь своего)

  12. Green

    Действительно, интересный код, только когда к таким вопросам обращаешься, удивляешься, и как я мог этого не знать…

    Все ведь элементарно. Слабый базовый фундамент, некоторых логика спасает.

  13. Mithras

    Ну дали бы хоть скопировать код, а то вытягивать его из исходного кода html-страницы это не дело!!!!


© Copyright. . I-Novice. All Rights Reserved. Terms | Site Map