为什么这个perl脚本运行速度慢于它的c对应?

byr*_*sos 0 c perl

我在perl中解决了这个琐碎的spoj问题.所以我提出了这个解决方案:

while ("0 0 0\n" ne ($string = <STDIN>)) {
  my @a = split ' ', $string;
  $a1 = $a[0];
  $a2 = $a[1];
  $a3 = $a[2];

  if($a2 - $a1 == $a3 - $a2) {
    $c  = $a2 - $a1;
    $a4 = $a3 + $c;
    print("AP ", $a4);
  }
  else {
    $c  = $a2 / $a1;
    $a4 = $a3 * $c;
    print("GP ", $a4);
  }

  print "\n";
}
Run Code Online (Sandbox Code Playgroud)

令我惊讶的是,它超过了时间限制.当我在C中尝试相同的事情时,它以最小的运行时间成功运行.这是C版本:

#include <stdio.h>

int main()
{
  int a1, a2, a3, a4, c;
  while (1) {
    scanf("%d %d %d", &a1, &a2, &a3);
    if (a1 == 0 && a2 == 0 && a3 == 0) break;
    if (a2 - a1 == a3 - a2) {
      c  = a2 - a1;
      a4 = a3 + c;
      printf("AP %d\n", a4);
    }
    else {
      c  = a2 / a1;
      a4 = a3 * c;
      printf("GP %d\n", a4);
    }
  }
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

那么,请你告诉我:

在这个问题上,perl真的比C慢了多少(至少200倍)吗?我怀疑它与输入和高级结构(如使用的数组)有关,或者我的代码中有一个错误导致程序停止.

amo*_*mon 6

您的基准测试有多种问题:

  • 这些计划并非绝对等同.
  • 至少你的Perl代码是unidiomatic(你甚至没有声明所有的变量!)

但最重要的是:

  • 该基准测试基于IO.
  • 如果输入不大,则启动时间也相关.

你做的计算绝对便宜,最昂贵的部分是读取和扫描输入.毫无疑问,最小C将比更高级别的PerlIO系统更快.

你没有意识到的另一件事是Perl是一种解释型语言,而C通常是编译的.在Perl的情况下,实际上有一个C程序查看某些数据结构(操作码),并根据某些标志进行加法,乘法,分支或比较.Perl变量是标量,实际上是SV*- 指向SV结构的指针.这些结构比一个结构大得多int.每次执行Perl程序时,perl解释器都会将整个源代码解析并编译为操作码.

另一方面,C编译为比操作码更有效的机器代码.这是在执行之前完成的,因此编译时间不会考虑到此基准测试中.由于这个原因,启动速度会更快.可以优化C以使用寄存器而不是堆上的位置,这使得简单的数据结构int更快.C标准库中的IO系统比Perl具有的复杂系统(解码层,缓冲)要简单得多.总而言之,C的间接水平低于Perl.

因为perl是一个C程序,所以这些间接级别会使Perl慢100倍.数学繁重的基准测试的差距越来越大(SV比整数更昂贵),但对于字符串操作index和正则表达式更接近.

为了记录,这是Perl代码的惯用版本.不,它不会跑得更快.

while (<>) {
  last if $_ eq "0 0 0\n";
  my ($a1, $a2, $a3) = split;

  if($a2 - $a1 == $a3 - $a2) {
    my $a4 = $a3 + $a2 - $a1;
    print "AP $a4\n";
  }
  else {
    my $a4 = $a3 * $a2 / $a1;
    print "GP $a4\n";
  }
}
Run Code Online (Sandbox Code Playgroud)