массивы — PHP многопоточный код Pthread медленнее, чем один поток

Я пытаюсь сделать очень простую, но многочисленную итерационную задачу. Я выбираю 7 случайных серийных номеров из массива из 324000 серийных номеров и помещаю их в другой массив, а затем выполняю поиск в этом массиве, чтобы выяснить, находится ли в нем конкретное число, выполняю другой сценарий и записываю, сколько раз искомое число находится в массив.

Это идет довольно быстро в одном потоке. Но когда я помещаю его в pthreads, даже один запущенный pthread работает в 100 раз медленнее, чем один поток. Рабочие не делятся никакими ресурсами (то есть получают всю информацию из своих собственных папок и записывают информацию в свои собственные папки). Fwrite узкие места — не проблема. Проблема с массивами, которые я отмечу ниже. Я сталкиваюсь с проблемой строки кэша, где массивы, хотя у них есть отдельные переменные, все еще разделяют ту же самую строку кэша? Вздох … очень признателен за вашу помощь в выяснении, почему массивы замедляют его работу.

<?php

class WorkerThreads extends Thread
{
private $workerId;
private $linesId;
private $linesId2;
private $c2_result;
private $traceId;

public function __construct($id,$newlines,$newlines2,$xxtrace)
{
$this->workerId = $id;
$this->linesId = (array) $newlines;
$this->linesId2 = (array) $newlines2;
$this->traceId = $xxtrace;
$this->c2_result= (array) array();
}

public function run()
{
for($h=0; $h<90; $h++) {
$fp42=fopen("/folder/".$this->workerId."/count.txt","w");

for($master=0; $master <200; $master++) {
// *******PROBLEM IS IN THE <3000 loop -very slow***********
$b=0;

for($a=0; $a<3000; $a++) {
$zex=0;

while($zex != 1) {
$this->c2_result[0]=$this->linesId[rand(0,324631)];
$this->c2_result[1]=$this->linesId[rand(0,324631)];
$this->c2_result[2]=$this->linesId[rand(0,324631)];
$this->c2_result[3]=$this->linesId[rand(0,324631)];
$this->c2_result[4]=$this->linesId[rand(0,324631)];
$this->c2_result[5]=$this->linesId[rand(0,324631)];
$this->c2_result[6]=$this->linesId[rand(0,324631)];

if(count(array_flip($this->c2_result)) != count($this->c2_result)) { //echo "duplicates\n";
$zex=0;
} else { //echo "no duplicates\n";
$zex=1;
//exit;
}
}

// *********PROBLEM here too !in_array statement, slowing down******
if(!in_array($this->linesId2[$this->traceId],$this->c2_result)) {
//fwrite($fp4,"nothere\n");
$b++;
}
}
fwrite($fp42,$b."\n");
}
fclose($fp42);

$mainfile3="/folder/".$this->workerId."/count_pthread.php";
$command="php $mainfile3 $this->workerId";

exec($command);
}
}
}

$xxTrack=0;

$lines = range(0, 324631);

for($x=0; $x<56; $x++) {
$workers = [];

// Initialize and start the threads
foreach (range(0, 8) as $i) {
$workers[$i] = new WorkerThreads($i,$lines,$lines2,$xxTrack);
$workers[$i]->start();
$xxTrack++;
}

// Let the threads come back
foreach (range(0, 8) as $i) {
$workers[$i]->join();
}

unset($workers);
}

ОБНОВЛЕННЫЙ КОД

Мне удалось ускорить исходный код в 6 раз с помощью предложений @tpunt. Самое главное, что я узнал, это то, что код замедляется вызовами rand (). Если бы я мог избавиться от этого, то скорость была бы в 100 раз быстрее. array_rand, mt_rand () и shuffle () еще медленнее. Вот новый код:

 class WorkerThreads extends Thread
{
private $workerId;
private $c2_result;
private $traceId;
private $myArray;
private $myArray2;

public function __construct($id,$xxtrace)
{
$this->workerId = $id;
$this->traceId = $xxtrace;
$c2_result=array();
}

public function run()
{
////////////////////THE WORK TO BE DONE/////////////////////////
$lines = file("/fold/considers.txt",FILE_IGNORE_NEW_LINES);
$lines2= file("/fold/considers.txt",FILE_IGNORE_NEW_LINES);

shuffle($lines2);

$fp42=fopen("/fold/".$this->workerId."/count.txt","w");

for($h=0; $h<90; $h++) {
fseek($fp42, 0);

for($master=0; $master <200; $master++) {
$b=0;

for($a=0; $a<3000; $a++) {
$zex=0;

$myArray = [];

$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;

while (count($myArray) !== 7) {
$myArray[rand(0,324631)] = true;
}

if (!isset($myArray[$lines2[$this->traceId]])) {
$b++;
}
}

fwrite($fp42,$b."\n");
}

$mainfile3="/newfolder/".$this->workerId."/pthread.php";
$command="php $mainfile3 $this->workerId";

exec($command);

}//END OF H LOOP
fclose($fp42);
}
}

$xxTrack=0;
$p = new Pool(5);for($b=0; $b<56; $b++) {
$tasks[$b]= new WorkerThreads($b,$xxTrack);
$xxTrack++;
}

// Add tasks to pool queue
foreach ($tasks as $task) {
$p->submit($task);
}

// shutdown will wait for current queue to be completed
$p->shutdown();

0

Решение

Ваш код просто невероятно неэффективен. Есть также ряд проблем с этим — я кратко остановился на некоторых из этих вещей ниже.

Во-первых, вы раскручиваете более 500 потоков (9 * 56 = 504). Это будет очень медленно, потому что многопоточность в PHP требует архитектуры без общего доступа. Это означает, что новый экземпляр интерпретатора PHP необходимо будет создавать для каждого создаваемого вами потока, где все классы, интерфейсы, признаки, функции и т. Д. Необходимо будет скопировать в новый экземпляр интерпретатора.

Возможно, еще важнее то, что ваши 3 вложенные for петли выполняют 54 миллиона итерации (90 * 200 * 3000). Умножьте это на 504 создаваемых потока, и вы скоро увидите, почему все становится вялым. Вместо этого используйте пул потоков (см. Pthreads ‘ Pool класс) с более скромным количеством потоков (попробуйте 8 и переходите оттуда) и сократите количество итераций, выполняемых для каждого потока.

Во-вторых, вы открываете файл 90 раз за поток (итого 90 * 504 = 45360). Вам нужен только один обработчик файлов на поток.

В-третьих, использование реальных массивов PHP внутри Threaded объекты делают их только для чтения. Так что в отношении $this->c2_result свойство, код внутри вашего вложенного while петля не должна даже работать. Не говоря уже о том, что следующая проверка не ищет дубликаты:

if(count(array_flip($this->c2_result)) != count($this->c2_result))

Если вы избегаете кастинга $this->c2_result свойство массива (поэтому делает его Volatile объект), тогда следующий код может вместо этого заменить while цикл:

$keys = array_rand($this->linesId, 7);
for ($i = 0; $i < 7; ++$i) {
$this->c2_result[$this->linesId[$keys[$i]]] = true;
}

Установив значения в качестве ключей в $this->c2_result мы можем удалить последующее in_array вызов функции для поиска через $this->c2_result, Это делается путем использования массива PHP в качестве хеш-таблицы, где время поиска ключа является постоянным временем (O (1)), а не линейным временем, требуемым при поиске значений (с in_array). Это позволяет нам заменить следующую медленную проверку:

if(!in_array($this->linesId2[$this->traceId],$this->c2_result))

со следующей быстрой проверкой:

if (!isset($this->c2_result[$this->linesId2[$this->traceId]]))

Но с учетом сказанного вы, кажется, не используете $this->c2_result недвижимость в другом месте. Таким образом (при условии, что у вас нет преднамеренно отредактированного кода, который его использует), вы можете полностью удалить его и просто заменить цикл while при проверке после него следующим:

$found = false;

foreach (array_rand($this->linesId, 7) as $key) {
if ($this->linesId[$key] === $this->linesId2[$this->traceId]) {
$found = true;
break;
}
}

if (!$found) {
++$b;
}

Помимо вышесказанного, вы также можете посмотреть на хранение данных, которые вы собираете в памяти (как некоторые свойства на Threaded объект), чтобы предотвратить дорогие записи на диск. Результаты могут быть агрегированы в конце, до закрытия пула.

Обновление на основе вашего обновления

Вы сказали, что rand функция вызывает значительное замедление. Хотя это может быть частью проблемы, я считаю, что это на самом деле весь код внутри вашего третьего вложенного for петля. Код внутри есть очень горячий код, потому что он выполняется 54 миллиона раз. Выше я предложил заменить следующий код:

$zex=0;

while($zex != 1) {
$c2_result[0]=$lines[rand(0,324631)];
$c2_result[1]=$lines[rand(0,324631)];
$c2_result[2]=$lines[rand(0,324631)];
$c2_result[3]=$lines[rand(0,324631)];
$c2_result[4]=$lines[rand(0,324631)];
$c2_result[5]=$lines[rand(0,324631)];
$c2_result[6]=$lines[rand(0,324631)];

$myArray = (array) $c2_result;
$myArray2 = (array) $c2_result;
$myArray=array_flip($myArray);

if(count($myArray) != count($c2_result)) {//echo "duplicates\n";
$zex=0;
} else {//echo "no duplicates\n";
$zex=1;
//exit;
}
}

if(!in_array($lines2[$this->traceId],$myArray2)) {
$b++;
}

с сочетанием array_rand а также foreach, После некоторых первоначальных испытаний выясняется, что array_rand на самом деле превосходно медленный. Но мое решение хэш-таблицы, чтобы заменить in_array вызов по-прежнему остается в силе. Используя массив PHP как хеш-таблицу (в основном, сохраняя значения в виде ключей), мы получаем постоянную производительность поиска по времени (O (1)), в отличие от линейного поиска по времени (O (n)).

Попробуйте заменить приведенный выше код следующим:

$myArray = [];

$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;
$myArray[rand(0,324631)] = true;

while (count($myArray) !== 7) {
$myArray[rand(0,324631)] = true;
}

if (!isset($myArray[$lines2[$this->traceId]])) {
$b++;
}

Для меня это привело к ускорению на 120%.

Что касается дальнейшей производительности, вы можете (как уже упоминалось выше, снова) сохранить результаты в памяти (как простое свойство) и выполнить запись всех результатов в конце run метод.

Кроме того, сборщик мусора для pthreads не является детерминированным. Поэтому его не следует использовать для извлечения данных. Вместо этого Threaded объект должен быть введен в рабочий поток, где данные, которые будут собраны, должны быть сохранены в этот объект. Наконец, вы должны закрыть бассейн после сборщик мусора (который, опять же, не должен использоваться в вашем случае).

1

Другие решения

несмотря на то, что неясно, что такое ваш код и что такое $ newlines и $ newlines2, так что я просто догадываюсь здесь …

что-то вроде этого ?
Идея состоит в том, чтобы избежать как можно большего количества fopen и fwrite в вашем цикле.
1 — открыть его только один раз в конструкции.
2 — соединить свою цепь в петле.
3 — написать это только один раз после цикла.

class WorkerThreads extends Thread {

private $workerId;
private $linesId;
private $linesId2;
private $c2_result;
private $traceId;
private $fp42;
private $mainfile3;

public function __construct($id, $newlines, $newlines2, $xxtrace) {
$this->workerId = $id;
$this->linesId = (array) $newlines;
$this->linesId2 = (array) $newlines2;
$this->traceId = $xxtrace;
$this->c2_result = array();

$this->fp42 = fopen("/folder/" . $id . "/count.txt", "w");
$this->mainfile3 = "/folder/" . $id . "/count_pthread.php";
}

public function run() {
for ($h = 0; $h < 90; $h++) {
$globalf42='';
for ($master = 0; $master < 200; $master++) {//<200
$b = 0;
for ($a = 0; $a < 3000; $a++) {
$zex = 0;
if ($zex != 1) {
for ($ii = 0; $ii < 6; $ii++) {
$this->c2_result[$ii] = $this->linesId[rand(0, 324631)];
}
$zex = (count(array_flip($this->c2_result)) != count($this->c2_result)) ? 0 : 1;
}
if (!in_array($this->linesId2[$this->traceId], $this->c2_result)) {
$b++;
}
}
$globalf42 .= $b . "\n";
}
fwrite($this->fp42, $globalf42);
fclose($this->fp42);
$command = "php $this->mainfile3 $this->workerId";
exec($command);
}
}

}

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector