Parallel-ForkManager,DBI。比之前分叉更快,但仍然太慢

时间:2015-03-06 18:47:38

标签: performance perl dbi fork

我有一个非常简单的任务来更新数据库。

my $pm = new Parallel::ForkManager(15);
for my $line (@lines){
    my $pid = $pm->start and next;
    my $dbh2 = $dbh->clone();
    my $sth2 = $dbh2->prepare("update db1 set field1=? where field2 =?");           
    my ($field1, $field2) = very_slow_subroutine();
    $sth2->execute($field1,$field2);
    $pm->finish;        
} 
 $pm->wait_all_children;    

我可以使用$ dbh2-> do,但我怀疑这是一个缓慢的原因。

有趣的是,它看起来很快就开始了这15个过程(或者我指定的任何过程),但是在那之后急剧减速,仍然明显快于没有分叉,但我希望更多......

编辑:

very_slow_subroutine是sub,它从Web服务获得答案。该服务可以在几秒到几秒的时间内回答。我要问十万次......我想做一个叉子的原因。

如果这很重要 - 我在Linux上。

2 个答案:

答案 0 :(得分:6)

Parallel :: ForkManager不会让事情变得更快,只是让你可以多次同时运行你的代码。为了从中获益,您必须设计并行代码。

这样想。你需要10分钟才能到达商店,购物,装车,回来和卸载它。你需要得到5个负载。你可以在50分钟内完成。这是串行工作。 10分钟* 5次一个接一个地= 50分钟。

让我们说你有四个朋友可以帮忙。你们都在同一时间开始营业。还有5次旅行,他们仍然需要10分钟,但因为你并行完成,总时间只有10分钟。

但无论你需要做多少次旅行或者有多少朋友可以提供帮助,它都不会少于10分钟。这就是为什么这个过程快速启动,每个人都进入他们的汽车并开车去商店,但是暂时没有任何事情发生,因为每个人都需要10分钟才能完成他们的工作。

这里也是一样的。你的循环体需要X时间才能运行。如果你迭代它Y次,它将需要X * Y现实世界的人类时间来运行。如果你并行运行Y次,理想情况下运行只需要X次。每个并行工作者仍然必须在X时间内执行循环的整个主体。

为了进一步加快速度,你必须打破very_slow_subroutine的大瓶颈,让 并行工作。您的SQL非常简单,您应该将精力集中在优化和并行性上。

让我们说商店非常接近,只需1分钟的车程(这是你的SQL更新),但购物,装货和卸货需要9分钟(这是very_slow_subroutine )。如果相反,你有5辆车和15个朋友。每辆车装3个人。开车往返商店需要同一时间,但现在有三个人正在一起做购物,装卸只需4分钟。现在每次旅行需要5分钟而不是10分钟。

这表示重新设计very_slow_subroutine以并行完成其工作。如果它只是一个大循环,你可以在这个循环上放置更多的工人。如果它是一系列缓慢的操作,你将不得不重新设计它以利用并行执行。

如果你使用太多的工人,你可能会堵塞系统,这取决于瓶颈是什么。如果它受CPU限制且你有2个CPU内核,你可能会看到性能提升最多3到5个工作者((cores * 2)+1是一个很好的经验法则),之后性能会下降因为CPU花费更多时间在流程之间切换而不是工作。如果瓶颈是IO,或者通常是数据库和网络调用的外部服务,您可以看到很多工作人员在解决问题时的效率很高。当一个进程正在等待磁盘或网络操作时,其他进程可以使用您的CPU。

答案 1 :(得分:4)

并行性能否发挥作用取决于瓶颈在哪里。如果具有4个内核的CPU是瓶颈,那么在最佳情况下,分配4个进程可能会导致事情在1/4左右完成,但是产生15个进程并不会改善事情。

如果更可能的是,您的瓶颈在于I / O,那么启动15个竞争相同I / O的进程并没有多大帮助,尽管在您有大量内存用作文件缓存的情况下,{ {3}}可能是可能的。

要探索系统的限制,请考虑以下程序:

#!/usr/bin/env perl

use strict;
use warnings;

use Parallel::ForkManager;

run(@ARGV);

sub run {
    my $count = @_ ? $_[0] : 2;
    my $pm = Parallel::ForkManager->new($count);
    for (1 .. 20) {
        $pm->start and next;
        sleep 1;
        $pm->finish;
    }
    $pm->wait_all_children;
}

我的古老笔记本电脑有一个带2个内核的CPU。让我们看看我得到了什么:

TimeThis :  Command Line :  perl sleeper.pl 1
TimeThis :  Elapsed Time :  00:00:20.735

TimeThis :  Command Line :  perl sleeper.pl 2
TimeThis :  Elapsed Time :  00:00:06.578

TimeThis :  Command Line :  perl sleeper.pl 4
TimeThis :  Elapsed Time :  00:00:04.578

TimeThis :  Command Line :  perl sleeper.pl 8
TimeThis :  Elapsed Time :  00:00:03.546

TimeThis :  Command Line :  perl sleeper.pl 16
TimeThis :  Elapsed Time :  00:00:02.562

TimeThis :  Command Line :  perl sleeper.pl 20
TimeThis :  Elapsed Time :  00:00:02.563

因此,使用最多20个进程运行使我的总运行时间超过2.5秒,每次睡眠时间为20秒。

另一方面,只需一个过程,睡一秒钟20次只需20多秒。这是一个巨大的改进,但它也表明当你有20个进程每个睡眠一秒钟时,管理开销超过150%。

这是并行编程的本质。对于你可以期待的东西,有许多正式的处理方法,但some improvement是必读的。