当内存使用率变高时 Perl 多线程变慢

Perl multithreading slower when memory usage getting high

大家好~我使用多线程在Perl中写了一个非常简单的代码。代码如下

#!/bin/perl

use strict;
use threads;
use Benchmark qw(:hireswallclock);

my $starttime;
my $finishtime;
my $timespent;
my $num_of_threads = 1;
my $total_size = 10000000;
my $chunk_size = int($total_size / $num_of_threads);

if($total_size % $num_of_threads){
        $chunk_size++;
}

my @threads = ();

$starttime = Benchmark->new;

for(my $i = 0; $i < $num_of_threads; $i++) {
        my $thread = threads->new(\&search);
        push (@threads, $thread);
}

foreach my $thread (@threads) {
        $thread->join();
}

my $finishtime = Benchmark->new;
$timespent = timediff($finishtime, $starttime);
print "$num_of_threads threads used in ".timestr($timespent)."\nDone!\n";

sub search{
        my $i = 0;
        while($i < $chunk_size){
            $i++;
        }

        return 1;
}

这段代码工作正常,因为当增加线程数时,它会 运行 更快。

但是,如果在中间添加额外的行,这将创建一个大数组,添加更多线程时代码将 运行 变慢。带有附加行的代码如下所示。

#!/bin/perl

use strict;
use threads;
use Benchmark qw(:hireswallclock);

my $starttime;
my $finishtime;
my $timespent;
my $num_of_threads = 1;
my $total_size = 10000000;
my $chunk_size = int($total_size / $num_of_threads);

if($total_size % $num_of_threads){
        $chunk_size++;
}

##########Additional codes##########
print "Preparing data...\n";
$starttime = Benchmark->new;

my @array = ();

for(my $i = 0; $i < $total_size; $i++){
        my $rn = rand();
        push(@array, $rn);
}

$finishtime = Benchmark->new;
$timespent = timediff($finishtime, $starttime);
print "Used ".timestr($timespent)."\n";
######################################

my @threads = ();

$starttime = Benchmark->new;

for(my $i = 0; $i < $num_of_threads; $i++) {
        my $thread = threads->new(\&search);
        push (@threads, $thread);
}

foreach my $thread (@threads) {
        $thread->join();
}

my $finishtime = Benchmark->new;
$timespent = timediff($finishtime, $starttime);
print "$num_of_threads threads used in ".timestr($timespent)."\nDone!\n";

sub search{
        my $i = 0;
        while($i < $chunk_size){
            $i++;
        }

        return 1;
}

我对 Perl 多线程中的这种行为感到很困惑。有谁知道这里可能出了什么问题吗?

谢谢!

您必须记住,当使用 ithreads(解释器线程)时,整个 Perl 解释器,包括代码和内存,都被克隆到新线程中。因此,要克隆的数据越多,所需的时间就越长。有一些方法可以控制克隆的内容;看看 threads perldoc。

您应该尽可能少地执行操作,甚至在生成线程之前不要加载很多模块。

如果您确实有大量数据将被所有线程使用,请与 threads::shared 共享。然后共享一个数据结构使用shared_clone()。你不能简单地 share() 任何东西,除了一个简单的变量。该共享变量只能包含普通标量或其他共享引用。

如果您要使用或抽取该数据,请使用 Thread::Queue 模块将其设为队列。它会自动共享值并负责锁定。生成工作线程池后,使用 Thread::Semaphore 控制它们。这样他们就不会在你给他们任何事情之前终止。您还可以防止竞争条件。

https://metacpan.org/pod/threads::shared

HTH

谢谢大家指点相关方向!我已经学习并尝试了不同的东西,包括如何使用共享和队列,这应该能够解决问题。所以我修改了脚本如下:

#!/bin/perl

use strict;
use threads;
use threads::shared;
use Thread::Queue;
use Benchmark qw(:hireswallclock);

my $starttime;
my $finishtime;
my $timespent;
my $num_of_threads = shift @ARGV;
my $total_size = 100000;
######Initiation of a 2D queue######
print "Preparing queue...\n";
$starttime = Benchmark->new;
my $queue = Thread::Queue->new();
for(my $i = 0; $i < $total_size; $i++){
        my $rn1 = rand();
        my $rn2 = rand();
        my @interval :shared = sort($rn1, $rn2);
        $queue->enqueue(\@interval);
}
$finishtime = Benchmark->new;
$timespent = timediff($finishtime, $starttime);
print "Used ".timestr($timespent)."\n";
#####################################
$starttime = Benchmark->new;
my $queue_copy = $queue; #Copy the 2D queue so that the original queue can be kept\
for(my $i = 0; $i < $num_of_threads; $i++) {
        my $thread = threads->create(\&search, $queue_copy);
}
foreach my $thread (threads->list()) {
        $thread->join();
}
$finishtime = Benchmark->new;
$timespent = timediff($finishtime, $starttime);
print "$num_of_threads threads used in ".timestr($timespent)."\nDone!\n";
#####################################
sub search{
        my $temp_queue = $_[0];
        while(my $temp_interval = $temp_queue->dequeue_nb()){
                #Do something
        }
        return 1;
}

我试图做的是,首先,制作一个数组队列,每个数组包含两个数字。制作了队列的副本,因为我想在通过它时保留原始队列。然后使用多线程完成复制的队列。但是,我仍然发现添加更多线程时它 运行 变慢,我不知道为什么。