上周偶然与fcicq讨论到一个关于perlcc的优化问题。 据说用perlcc将perl程序编译成C程序后再用gcc -O3进行优化,速度可能会快一些。 于是就测了测,顺便试了试其他语言的情况。

测试程序是Ackermann函数。 也许用它来做benchmark不太合适,但毕竟这是个纯数学+多次递归+耗时的运算,也能反映一定问题吧。

先来看Perl的原版。

$ cat ack.pl
#!/usr/bin/perl

sub ackermann {
  my ( $m, $n ) = @_;

  return $n + 1 if $m == 0;
  return ackermann( $m - 1, 1 ) if $n == 0;
  return ackermann( $m - 1, ackermann( $m, $n - 1 ) );
}

print ackermann( 3, 10 ), "\n";

$ time ./ack.pl
8189

real    1m5.044s
user    1m4.412s
sys     0m0.620s

结果约为65秒。然后用perlcc编译并gcc -O3优化试试:

$ cp /usr/lib/perl5/5.8.8/i386-linux-thread-multi/auto/DynaLoader/DynaLoader.a .
$ ar xv DynaLoader.a          # 先弄个必要的DynaLoader.o否则会连接错误
$ perl -c -o ack-perlcc.c ack.pl
$ gcc -O3 -c -o ack-perlcc.o `perl -MExtUtils::Embed -e ccopts` ack-perlcc.c
$ gcc -o ack-perlcc `perl -MExtUtils::Embed -e ldopts` DynaLoader.o ack-perlcc.o
$ time ./ack-perlcc
8189
real    1m3.487s
user    1m3.012s
sys     0m0.484s

用了63秒,跟perl是同一数量级的。可见这个perlcc之后没什么效果。

遗憾之余顺手写了个纯C版本:

$ cat myack.c
#include <stdio.h>

int ackermann(int m, int n);

int main() {
  int result = ackermann(3, 10);
  printf("%d\n", result);
  return 0;
}

int ackermann(int m, int n) {
  if (m == 0) return n+1;
  if (n == 0) return ackermann(m-1, 1);
  return ackermann(m-1, ackermann(m, n-1));
}

$ gcc -O3 -o myack myack.c
$ time ./myack
8189

real    0m0.231s
user    0m0.228s
sys     0m0.004s

哇!0.23秒,比perl语言快了280倍以上。看来谈到效率时果然C语言才是王道。 也难怪为什么C程序员的待遇那么高了。

当然这个程序是纯粹的数学运算,发挥不出perl的长处,才会让perl效率如此低吧。

PHP也有同样的问题。还是这个Ackermann函数,用纯PHP写出来的效率很低, 但如果将函数写成php extension再调用,效率几乎等同于C语言的效率。 于是得出个结论,复杂的算法还是不要用PHP直接实现,而是写成extension吧。