Perl中while(each)和foreach之间的性能差异

时间:2013-12-16 06:59:53

标签: perl foreach

我测试的哈希包含大约70000所大学,每所大学包含大约20名学生。 我尝试了5次,结果如下。 foreach性能和(每个)性能有很大差异。为什么会这样?

带循环的代码:

while ( my ($college_code, $college_info_hr) = each (%{$college_data_hr}) ) {
    while ( my ($student_num, $student_info_hr) = each (%{$college_info_hr->{'students'}}) ) {
        if($student_num < 104000) { ## Delete the info of students before 2004.
            delete $college_info_hr->{'students'}{$student_num};
        }
    }
}

使用foreach循环的代码:

foreach my $college_code (keys %{$college_data_hr}) {
    foreach my $student_num (keys %{$college_data_hr->{$college_code}{'students'}}) {
        if($student_num < 104000) { ## Delete the info of students before 2004.
            delete $college_data_hr->{$college_code}{'students'}{$student_num};
        }
    }
}

当大学数量为70,000时,执行时间为:

对于带while循环的代码(间隔时间以秒为单位):

间隔时间:2.186621

间隔时间:2.058644

间隔时间:2.055645

间隔时间:2.101637

间隔时间:2.124632

对于带有foreach循环的代码:(间隔时间以秒为单位)

间隔时间:1.341768

间隔时间:1.436751

间隔时间:1.346529

间隔时间:1.302775

间隔时间:1.356765

当大学数量为248,000时,执行时间为:

(while循环的执行时间)

间隔时间:9.084427

间隔时间:8.438684

间隔时间:9.329338

间隔时间:9.169687

(foreach循环的执行时间)

间隔时间:5.502048

间隔时间:6.386692

间隔时间:5.596032

间隔时间:5.620144

2 个答案:

答案 0 :(得分:7)

foreach版本仅在每个大学时取消引用$college_data_hr->{$college_code}{'students'} hashref,因此比每个学生需要执行一次的while版本更快。

foreach版本可能会使用更多内存,因为它需要构建包含每个哈希键的临时列表。

Data::Alias可能会帮助您加快while解决方案的速度。我没有对此进行基准测试,但它应该相当快......

use Data::Alias;

while ( my ($college_code, $college_info_hr) = each %$college_data_hr ) {
    alias ( my %students = %{$college_info_hr->{'students'}} );
    while ( my ($student_num, $student_info_hr) = each %students ) {
        if ($student_num < 104000) { ## Delete the info of students before 2004.
            delete $students{$student_num};
        }
    }
}

答案 1 :(得分:2)

每次通过while循环都需要执行大量操作(enterleave以外的所有操作都适用于您的代码。)

>perl -MO=Concise,-exec -e"my ($college_code, $college_info_hr) = each (%{$college_data_hr})"
1  <0> enter
2  <;> nextstate(main 2 -e:1) v:{
3  <0> pushmark s
4  <#> gv[*college_data_hr] s
5  <1> rv2sv sKM/DREFHV,1
6  <1> rv2hv[t4] lKRM/1
7  <1> each lK/1
8  <0> pushmark sRM*/128
9  <0> padsv[$college_code:2,3] lRM*/LVINTRO
a  <0> padsv[$college_info_hr:2,3] lRM*/LVINTRO
b  <2> aassign[t5] vKS
c  <@> leave[1 ref] vKP/REFC
-e syntax OK

将值复制到$college_code$college_info_hr中。从好的方面来说,它们不是字符串。

你的foreach循环没有那个。每次传递都会发生的唯一事情是更改$college_code别名的内容。很快。当然,缺点是会占用更多内存。


替代方案:

for my $college_code (keys %$college_data_hr) {
    my $students = $college_data_hr->{$college_code}{students};
    delete @$students{ grep $_ < 104000, keys %$students };
}