Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PHP Pthread多线程代码比单线程慢_Php_Arrays_Multithreading_Pthreads - Fatal编程技术网

PHP Pthread多线程代码比单线程慢

PHP Pthread多线程代码比单线程慢,php,arrays,multithreading,pthreads,Php,Arrays,Multithreading,Pthreads,我试图做一个非常简单但大量迭代的任务。我从一个324000个序列号的数组中选择7个随机序列号,并将它们放在另一个数组中,然后搜索该数组以查看是否有特定的序列号在其中,执行另一个脚本并写出在数组中查找的序列号的次数 这在单线程中运行得相当快。但当我把它放在pthreads中时,即使一个pthread运行也比单个线程慢100倍。工作人员没有共享任何资源,即从他们自己的文件夹中获取所有信息并将信息写入他们自己的文件夹。写入瓶颈不是问题所在。问题在于我在下面提到的数组。我是否遇到了缓存线问题,阵列虽然有

我试图做一个非常简单但大量迭代的任务。我从一个324000个序列号的数组中选择7个随机序列号,并将它们放在另一个数组中,然后搜索该数组以查看是否有特定的序列号在其中,执行另一个脚本并写出在数组中查找的序列号的次数

这在单线程中运行得相当快。但当我把它放在pthreads中时,即使一个pthread运行也比单个线程慢100倍。工作人员没有共享任何资源,即从他们自己的文件夹中获取所有信息并将信息写入他们自己的文件夹。写入瓶颈不是问题所在。问题在于我在下面提到的数组。我是否遇到了缓存线问题,阵列虽然有单独的变量,但仍然共享同一缓存线?唉……非常感谢你的帮助,让我们弄明白为什么阵列会让它慢下来

<?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);
}
更新代码

在@tpunt建议的帮助下,我将原始代码的速度提高了6倍。最重要的是,我学到的是,调用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();

尽管不清楚您的代码以及$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);
    }
}

}您的代码效率太低了。它也有一些问题——下面我对其中一些问题进行了快速分解

首先,您正在旋转500多个线程9*56=504。这将非常缓慢,因为PHP中的线程需要一个无共享架构。这意味着需要为您创建的每个线程创建一个新的PHP解释器实例,其中所有类、接口、特征、函数等都需要复制到新的解释器实例中

不过,更重要的是,您的3个嵌套for循环正在执行5400万次迭代90*200*3000。将其乘以正在创建的504个线程,您很快就会明白为什么事情会变得缓慢。相反,使用一个线程池—请参阅pthreads的pool类,其中包含少量线程—请尝试8,然后从那里开始,并减少每个线程执行的迭代次数

其次,每个线程打开一个文件90次,因此总共90*504=45360。每个线程只需要一个文件处理程序

第三,利用线程对象内部的实际PHP数组使它们成为只读的。因此,关于$this->c2_result属性,嵌套while循环中的代码甚至不应该工作。更不用说以下检查不查找重复项:

if(count(array_flip($this->c2_result)) != count($this->c2_result))
如果避免将$this->c2_result属性强制转换为数组,从而使其成为易失性对象,则以下代码可以替代while循环:

通过以下快速检查:

if (!isset($this->c2_result[$this->linesId2[$this->traceId]]))
尽管如此,您似乎没有在其他任何地方使用$this->c2_result属性。因此,假设您没有故意编辑使用它的代码,您可以将其全部删除,并简单地用以下内容替换while循环at check after:

$found = false;

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

if (!$found) {
    ++$b;
}
$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++;
}
除上述内容外,您还可以考虑将正在收集的数据存储在内存中,作为线程对象上的某些属性,以防止昂贵的磁盘写入。在关闭池之前,可以在最后聚合结果

更新基于您的更新

您已经说过,兰德函数导致了严重的减速。虽然这可能是问题的一部分,但我相信它实际上是第三个嵌套for循环中的所有代码。里面的代码是非常热门的代码,因为它执行了5400万次。我在上面建议您替换以下代码:

$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++;
}
使用数组和foreach的组合。经过一些初步测试,结果表明array_rand确实非常慢。但我用哈希表替代in_数组调用的解决方案仍然有效。通过利用PHP数组作为哈希表(基本上是将值存储为键),我们可以获得恒定时间的查找性能O1,而不是上的线性时间查找

尝试用以下代码替换上述代码:

$found = false;

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

if (!$found) {
    ++$b;
}
$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的垃圾收集器不是确定性的。因此,不应使用它来检索数据。相反,应该将线程对象注入工作线程,在工作线程中收集的数据应该保存到此obje
计算机断层扫描。最后,您应该在垃圾收集后关闭池,这同样不应该在您的案例中使用。

$newlines和$newlines2都是数组,一个数组是搜索的数字,另一个数组是随机从中提取数字并放入7个数组数据点的数组。我会尝试你的建议并报告…谢谢@cpugourouTried您的代码,不幸的是这不是一个fwrite问题。这是我在上面代码中指出的两个数组区域中的数组问题。在第一个循环200和第二个循环200之间的阵列区域中,你能看到任何东西吗?你绝对需要继续增加阵列吗?尝试为每个主循环重置$this->c2\u结果。感谢您的反馈!我正在研究你的建议…我唯一不明白的是,你指出我有500个工作线程同时运行,但我只看到9个工作线程…不就是unset$workers命令,每次我循环时销毁9个工作线程,$workers=[];命令,启动9个新线程以替换旧的?@user3152377您没有同时运行500个线程,但您创建了500个线程。具体来说,您的循环创建了9个线程的批处理,共56次。当您加入衍生线程时,它们将被销毁。如果您使用了线程池,那么您只需创建这9个线程一次,并在您的外部循环的所有56次迭代中重用它们。感谢您对我进行整理!我发布了更新的代码,大约快了6-8倍。我保留了在数组中插入随机数的原始代码,因为一旦我完成了所有建议的替换,它实际上会更快,但我确实删除了所有这些fwrite,放入了一个新池,而不是旋转线程并销毁500次,修复了不工作的重复测试,并将数组作为对象保留,仅在最后进行强制转换@tpuntrand调用也是一个真正的问题,我测试了脚本,如果我能摆脱rand,它会快100倍。我试过array_rand,速度太慢了……还有,试过mt_rand和用array_slice洗牌。没有雪茄配这些。你知道有什么更快的方法可以得到一个随机数吗@tpunt@user3152377我已经根据你的更新更新了我的回复,我也把你的更新放在了你原来的帖子上,只是为了把所有的东西都放在一起。