如何通过海量数据提高Perl的性能?

时间:2010-01-07 09:26:25

标签: performance perl

我正在寻找改进我的算法来分配大量数据之谜。 如果有人能想到一些让它运行得更快的好主意,我会很感激。

我有8个文件包含2个参数:

1)起点

2)终点

这些类型的数据在文件中以不同的数字重复。 意味着我可以拥有一个包含200个起点+ 200个终点的文件,或者一个包含50000个起点+ 50000个终点的文件。

好的,这些数据可能性反映了X轴数据点。每个点的开始/结束将被分类到X_sort_array。

问题始于Y轴数据点。

为了创建Y轴数据点,我使用了一些在X_sort_array点上运行的算法,并检查每个点是否在所有起始点个性之间。如果是为Y_array [x_point]添加一个......依此类推......

我会在这里粘贴我的大部分代码,如果有一种语法/方式来提高效率,我很乐意知道。

当sort_all_legth大于50000时 - 慢动作开始了:

    my $sort_all_length = @{$values{"$tasks_name[$i]\_sort\_allvals"}};
    my $core_count = 0;
    my $m=0;

    for my $k(0 .. $sort_all_length-1){



    if (($values{"$tasks_name[$i]\_sort\_allvals"}->[$k] eq $values{"$tasks_name[$i]\_sort\_allvals"}->[$k-1])
& ($k != 0) ) {
}

    else {
    my $pre_point_flag;
    # CHEACK PREVIUES POINT:
    for my $inst_num(0 .. $sort_all_length/2-1){
     $pre_point_flag=1;
     if ( ($values{"TID$i\_$tasks_name[$i]\_SHL"}->[$inst_num] <=    $values{"$tasks_name[$i]\_sort\_allvals"}->[$k]-1) 
   & ($values{"$tasks_name[$i]\_sort\_allvals"}->[$k]-1 <= $values{"TID$i\_$tasks_name[$i]\_EHL"}->[$inst_num]) )  {
    $pre_point_flag=0;
    last;
 }
}
if ($pre_point_flag eq 1){
 push(@{$values{"$tasks_name[$i]\_Y"}},0);
 push(@{$values{"$tasks_name[$i]\_X"}},$values{"$tasks_name[$i]\_sort\_allvals"}->[$k]-1);
}

# CHEACK DEFINE POINT:
for my $inst_num(0 .. $sort_all_length/2-1){
 if ( ($values{"TID$i\_$tasks_name[$i]\_SHL"}->[$inst_num] <= $values{"$tasks_name[$i]\_sort\_allvals"}->[$k]) 
   & ($values{"$tasks_name[$i]\_sort\_allvals"}->[$k] <= $values{"TID$i\_$tasks_name[$i]\_EHL"}->[$inst_num]) )  {
    $core_count++;
 }

}
push(@{$values{"$tasks_name[$i]\_Y"}},$core_count);
push(@{$values{"$tasks_name[$i]\_X"}},$values{"$tasks_name[$i]\_sort\_allvals"}->[$k]);

# CHEACK LATER POINT:
for my $inst_num(0 .. $sort_all_length/2-1){
 $pre_point_flag=1;
 if ( ($values{"TID$i\_$tasks_name[$i]\_SHL"}->[$inst_num] <= $values{"$tasks_name[$i]\_sort\_allvals"}->[$k]+1) 
   & ($values{"$tasks_name[$i]\_sort\_allvals"}->[$k]+1 <= $values{"TID$i\_$tasks_name[$i]\_EHL"}->[$inst_num]) )  {
    $pre_point_flag=0;
    last;
 }
}
if ($pre_point_flag eq 1){
 push(@{$values{"$tasks_name[$i]\_Y"}},0);
 push(@{$values{"$tasks_name[$i]\_X"}},$values{"$tasks_name[$i]\_sort\_allvals"}->[$k]+1);
}

if ($y_max_val < $core_count){
 $y_max_val = $core_count;
}

$core_count = 0;
$m++;
}



}

具有良好性能的小数据示例:

(糟糕的表现是I.P大小为50000!)

  

数据库:task:byte_position

     

数据库:二进制文件大小:3216

     

数据库:开始/结束值的数量= 804

     

数据库:计算已排序的I.P数组中的活动核心(大小:1608)...

     

数据库:I.P数组值:

     

375127997,375135023,375177121,375177978,375225484,375226331,   375273745,375274563,375320063,375325255,375372479,375377085,   375422115,375422896,375473198,375474058,375517412,375518169,   375561967,375562760,375606301,375607092 ...

TEST示例输入/输出:

输入是X轴上的起点和终点:

  

16255

     

16255

     

16255

     

100355

     

200455

     

数据库:任务:TEST

     

数据库:二进制文件大小:20

     

数据库:开始/结束值的数量= 5

     

数据库:排序的I.P数组(X轴):

     

16,16,16,100,200,255,255,255,355,455

这些点中的每一个都称为有趣点(在X轴上)。

为了计算Y值,我用每个I.P。

绘制一条垂直线

然后我计算有多少输入线(起点/终点)通过第一条垂直线。

结果是第一个X I.P

的第一个Y点
  

数据库:计算已排序的I.P数组中的活动核心(大小:10)......

输出向量:

  

数据库:排序核心数组(Y轴):

     

0,3,4,5,5,2,1,0

在我的算法中,我在每个开始/结束一个块的I.P之前和之后添加了另一个点。

你可以在开头/结尾看到零!

3 - 表示在X点16处有3条切片线

4 - 表示在X点100处有4条切片线

5 - (1)表示在X点200处有5条切片线

5 - (2)表示在X点255处有5条切片线

2 - 表示在X点355处有2条切片线

1 - 表示在X点455处有1条切片线

希望这些扩展示例能让您更加了解算法。 :)

我将重建代码以提高可读性。

谢谢, Yodar。

6 个答案:

答案 0 :(得分:3)

提高绩效的一种非常简单的方法就是简单地学习更好的编码实践。

你忽略的一个原则是:不要重复自己。

你在那里重复了一些代码,迫使Perl一次又一次地评估同一个表达式。一个例子就是这个字符串:

"$tasks_name[$i]\_sort\_allvals"

它在那里使用了大约10次。这意味着每次使用它时perl都会引用数组,以防它发生变化,并将该字符串放在一起。它可能看起来不多,但最终会加起来。

另一个例子是:

$values{"$tasks_name[$i]\_sort\_allvals"}->[$k]

它也使用了10次,而$ k实际上改变了每个循环,对于每次循环运行,整个表达式的值是相同的。在循环开始时将它存储在单个标量中会更快,然后在其余部分中使用该标量,因为您避免强制perl每个循环解析参考10次。

答案 1 :(得分:3)

我跟着它有点麻烦,但听起来你正在为每个轴X点重复从起始for my $var (0..whatever) { ... last if $done; }搜索一个(方便地预分类)数据文件-X点数组。

总计Shlemiel the Painter algorithm。这可能是性能问题的根源。你应该尝试避免你不需要再做的部分搜索。

答案 2 :(得分:1)

我试图了解你的程序,但我的大脑有点ch咽。您能为我们提供示例输入,您想要做什么的一些解释,以及良好的示例输出吗?从你的例子来看,我无法做出正面或反面。

你说这是一些有效的输入:

database: task: TEST

database: binary file size: 20

database: numbers of start/end values = 5

database: sorted I.P array (X axis):

16,16,16,100,200,255,255,255,355,455

database: counting active cores in sorted I.P array (size: 10)...

这是有效的输出:

database: sorted cores array (Y axis):

0,3,4,5,5,2,1,0

但我不明白。注意解释你想要达到的目标?

答案 3 :(得分:1)

根本不需要这个任务的间隔时间。您只需对间隔的开始和结束进行排序,然后合并此排序的数组并计算打开的间隔数。绝对是O(NlogN)算法,并以秒为单位处理50k间隔。

#!/usr/bin/env perl

use strict;
use warnings;

my (@starts, @ends);

while(<>) {
    chomp;
    my($start, $end) = split',';
    push @starts, $start;
    push @ends, $end;
}

@starts = sort { $a <=> $b } @starts;
@ends = sort { $a <=> $b } @ends;

my $y = 0;
my ($x, @y, @x);
while (@starts) {
    my $x = $starts[0] <= $ends[0] ? $starts[0] : $ends[0];
    while ($starts[0] == $x) {
        $y++;
        shift @starts;
        last unless @starts;
    }
    push @x, $x;
    push @y, $y;
    while ($ends[0] == $x) {
        $y--;
        shift @ends;
        last unless @ends;
    }
}

while (@ends) {
    my $x = $ends[0];
    push @x, $x;
    push @y, $y;
    while ($ends[0] == $x) {
        $y--;
        shift @ends;
        last unless @ends;
    }
}

die "Unbalanced!" if $y;

$" = ',';
print "0,@y,0\n";

答案 4 :(得分:0)

根据你在这里说的话:

  

为了创建Y轴数据点,我使用了一些在X_sort_array点上运行的算法,并检查每个点是否在所有起始点个性之间。如果是为Y_array [x_point]添加一个......依此类推......

并假设这是您的问题,那么修改后的二进制搜索算法将在O(log n)中工作,或者n = 1_000_000约为6步:

sub binary_range_search {
    my ( $range, $ranges ) = @_;
    my ( $low, $high ) = ( 0, @{$ranges} - 1 );
    while ( $low <= $high ) {

        my $try = int( ( $low + $high ) / 2 );

        $low  = $try + 1, next if $ranges->[$try][1] < $range->[0];
        $high = $try - 1, next if $ranges->[$try][0] > $range->[1];

        return $ranges->[$try];
    }
    return;
}

在此版本中,您要查找范围@$range是否与@$ranges中的任何范围重叠。您可以对其进行修改以查找所有范围内单个点的重叠。这就是你要找的东西吗?

答案 5 :(得分:-2)

可能更好的方法是将数据加载到BD,在其字段上创建索引并运行SQL SELECT start,stop FROM X WHERE Y介于start,stop;

之间

BD有很好的二进制搜索算法。可能比你的好。

另一种方法是将数据从字符串转换为二进制,并且可以使用C静态数组来操作它。 喜欢

struct point {
   int start, int stop      
}  

point array[8000];
read array form file;
for (int i=0; i<8000; i++)
{
}

你会想知道它是多么快速