2016-11-09 129 views
1

我有一个perl程序,试图将一堆文件从一种格式转换为另一种格式(通过命令行工具)。它工作正常,但速度太慢,因为它一次转换文件。perl fork()似乎没有利用核心,但只有cpu

我研究并利用了fork()机制,试图产生所有希望利用cpu/cores的子fork的转换。

编码完成并测试,它确实提高了性能,但不符合我的预期。 当的/ proc/cpuinfo中寻找,我有这样的:

> egrep -e "core id" -e ^physical /proc/cpuinfo|xargs -l2 echo|sort -u 
physical id : 0 core id : 0 
physical id : 0 core id : 1 
physical id : 0 core id : 2 
physical id : 0 core id : 3 
physical id : 1 core id : 0 
physical id : 1 core id : 1 
physical id : 1 core id : 2 
physical id : 1 core id : 3 

这意味着我有2个CPU和四核的每一个?如果是这样,我应该能够分出8个分支,假设我应该能够在1分钟内完成8分钟的工作(每个文件1分钟,8个文件)以完成(8个分叉,每个分叉1个文件)。

但是,当我测试运行这个,它仍然需要4分钟才能完成。看起来它只用了2个CPU,而不是内核?

因此,我的问题是:

  1. 这是真的,Perl的fork()的唯一平行它的基础上的CPU,但不是核心?或者,也许我没有做对吗?我只是使用fork()和wait()。没什么特别的。我会假设perl的fork()应该使用核心,是否有一个简单的bash/perl,我可以写来证明我的操作系统(即RedHat 4),Perl也不是这种症状的罪魁祸首?

补充:

我甚至尝试运行以下命令多次模拟多个处理和监控HTOP。

while true; do echo abc >>devnull; done & 

不知何故htop告诉我我有16个内核?然后当我产生4个以上的while循环时,我看到其中4个使用〜100%cpu。当我产生更多的时候,他们都开始平均降低CPU利用率。 (例如8次处理,请参阅htop中的8 bash,但每次使用约50%)这是否意味着什么?

提前感谢。我试过谷歌周围,但无法找到明显的答案。


编辑:2016年11月9日

这里是Perl代码的提取物。我很想看看我在这里做错了什么。

my $maxForks = 50; 
my $forks = 0; 
while(<CIFLIST>) { 
    extractPDFByCIF($cifNumFromIndex, $acctTypeFromIndex, $startDate, $endDate); 
} 
for (1 .. $forks) { 
    my $pid = wait(); 
    print "Child fork exited. PID=$pid\n"; 
} 

sub extractPDFByCIF { 
    # doing SQL constructing to for the $stmt to do a DB query 
    $stmt->execute(); 

    while ($stmt->fetch()) { 
     # fork the copy/afp2web process into child process 
     if ($forks >= $maxForks) { 
      my $pid = wait(); 
      print "PARENTFORK: Child fork exited. PID=$pid\n"; 
      $forks--; 
     } 
     my $pid = fork; 
     if (not defined $pid) { 
      warn "PARENTFORK: Could not fork. Do it sequentially with parent thread\n"; 
     } 
     if ($pid) { 
      $forks++; 
      print "PARENTFORK: Spawned child fork number $forks. PID=$pid\n"; 
     }else { 
      print "CHILDFORK: Processing child fork. PID=$$\n"; 
      # prevent child fork to destroy dbh from parent thread 
      $dbh->{InactiveDestroy} = 1; 
      undef $dbh; 

      # perform the conversion as usual 
      if($fileName =~ m/.afp/){ 
        system("file-conversion -parameter-list"); 
      } elsif($fileName =~ m/.pdf/) { 
        system("cp $from-file $to-file"); 
      } else { 
        print ERRORLOG "Problem happened here\r\n"; 
      } 
      exit; 
     } 
     # end forking 

    $stmt->finish(); 
    close(INDEX); 
} 
+6

Perl只是使用'fork'系统调用。你应该在C中看到完全相同的行为。 – ikegami

+2

没有看到代码就很难调试。 – shawnhcorey

+0

您的代码可能有问题;显示它,我们可以提供帮助。 – ysth

回答

4

fork()产生一个新的过程 - 与现有的过程相同,状态相同。没有更多,不少。内核调度它并在任何地方运行。

如果你没有得到你期望的结果,我会建议一个更可能的限制因素是你正在从你的磁盘子系统读取文件 - 磁盘速度很慢,争夺IO实际上并没有实现他们更快 - 如果有任何相反的话,因为它强制追加驱动器搜索和更少缓存。

所以具体是:

1 /否,fork()确实没有什么比你的克隆过程多。

2 /很大程度上没有意义,除非你想在shell中重写大部分算法。没有真正的理由认为它会有所不同。

要从编辑遵循:

  • system('file-conversion')看起来极像是一个基于IO的过程,这将是你的磁盘IO的限制。如您的cp

  • 您是否考虑过Parallel::ForkManager这大大简化了分叉位?

  • 作为一个较小的风格点,你应该使用3 arg'open'。

    #!/usr/bin/env perl 
    use strict; 
    use warnings; 
    use Parallel::ForkManager; 
    
    my $maxForks = 50; 
    
    my $manager = Parallel::ForkManager->new($maxForks); 
    
    while ($ciflist) { 
    
        ## do something with $_ to parse. 
    
        ##instead of: extractPDFByCIF($cifNumFromIndex, $acctTypeFromIndex, $startDate, $endDate); 
    
        # doing SQL constructing to for the $stmt to do a DB query 
        $stmt->execute(); 
    
        while ($stmt->fetch()) { 
    
         # fork the copy/afp2web process into child process 
         $manager->start and next; 
         print "CHILDFORK: Processing child fork. PID=$$\n"; 
    
         # prevent child fork to destroy dbh from parent thread 
         $dbh->{InactiveDestroy} = 1; 
         undef $dbh; 
    
         # perform the conversion as usual 
         if ($fileName =~ m/.afp/) { 
          system("file-conversion -parameter-list"); 
         } elsif ($fileName =~ m/.pdf/) { 
          system("cp $from-file $to-file"); 
         } else { 
          print ERRORLOG "Problem happened here\r\n"; 
         } 
    
         # end forking 
         $manager->finish; 
        } 
        $stmt->finish(); 
    
    } 
    
    $manager->wait_all_children; 
    
-1

你的目标是并行在使用多内核作为独立资源的方式您的应用程序。你想要实现的是多线程,特别是Perl的ithreads,它们使用了对底层系统的fork()函数的调用(并且因此重为。您可以从perlthrtut向您自己传授Perl的多线程方式。从perlthrtut报价:

当创建一个新的Perl的线程,所有与当前线程相关的数据被复制到新的线程,随后私人到新线程!这与Unix进程派生的情况类似,只不过在这种情况下,数据只是在同一进程中复制到内存的不同部分,而不是发生真正的派生。

话虽这么说,关于您的问题:

  1. 你不这样做是正确的(对不起)。 [请参阅我的评论...]对于多线程,您不需要自己拨打fork(),但Perl会为您执行此操作

  2. 您可以检查您的Perl解释器是否构建了线程支持,例如,由perl -V(注意大写V)和看消息。如果没有什么可以看到线程,那么你的Perl解释器不能使用Perl-multithreading。

的原因,你的应用程序已经快,即使只有一个CPU核心采用fork()很可能是当一个进程必须等待缓慢的资源,比如文件系统中的另一个进程可以使用相同的核心为计算资源。

+0

为什么downvote? – VCSEL

+0

不是我的失望,但一些'perl'程序员不喜欢线程。 – Sobrique

+0

不是我的downvote,但线程与fork没有任何关系(除了在windows上它可能使用最初作为模拟windows的模拟开始的东西) – ysth