Chinaunix首页 | 论坛 | 博客
  • 博客访问: 549945
  • 博文数量: 81
  • 博客积分: 5015
  • 博客等级: 大校
  • 技术积分: 866
  • 用 户 组: 普通用户
  • 注册时间: 2005-03-25 09:08
个人简介

www.cloud86.cn

文章分类

全部博文(81)

文章存档

2017年(2)

2014年(1)

2011年(1)

2007年(5)

2006年(31)

2005年(41)

我的朋友

分类:

2006-01-23 09:39:19

Perl是强大的语言,是强大的工具,也是一道非常有味道的菜:-) 利用很多perl 的特性,可以实现一些非常有趣而实用的功能。

利用Perl开发一些服务应用时,有时会遇到性能或资源占用的问题,如何解决呢?以下是自己过去开发实践的一些经验,几个主要的技巧分别是:

  • 巧用require装载模块
  • 使用系统函数及XS化模块
  • 自写低开销模块
  • 优化正则表达式
  • 善用BSD socket

巧用require装载模块

为避免程序一启动就加载大量模块,降低启动速度,可以在必要的时候再装载模块,这时候就是require大派用场的时候了。

如:


#!/usr/bin/perl -w
use pre_load_module;

# Initialize some thing
init_args();

# if $use_this_module is true, load the Module
if($use_this_module) {
     require Module;
}

上述代码中,如果变量$use_this_module设置了,那么才加载Module,如果没设置则不需要加载,实现了:use on demand的功能。在CGI应用程序中,这相当有用,如果每次请求(fork)都加载大量无用模块的话,响应速度会有所降低,而在特定场合才加载一些模块将加块启动、解析的速度。

再看一个例子:


#!/usr/bin/perl
my $pid = fork or die "can't fork:$!\n";
if($pid) {
     print "i'm father\n";
     sleep;
}else {
     print "i'm child\n":
     require IO::Socket;
     sleep;
}

上述代码中,如果在程序一开始就用use 来载入IO::Socket模块,那么子/父进程都加载了该模块,通过top命令发现子父进程大小都是3.07MB;如果只在子进程里加载,则只在子进程里有效,内存的消耗将降低,top命令发现子进程3.04MB,父进程变为1.4MB。

使用系统函数及XS化模块

Perl内建的系统函数及用c编写的perl XS扩展模块的速度和效率都比纯perl的实现要好得多。在性能要求较高的场合(如开发Application Server,Network Server等),可以考虑使用这些内建函数或XS化模块。

如Socket就比IO::Socket的内存消耗要低,XS编写的Data::Dumper就比纯Perl的Data::Dumper要快4-5倍。

此外,一些简单的任务并没必要使用Perl 模块,如获得主机IP地址就大可不必载入庞大的Net::DNS而只是使用gethostbyname()系统函数即可。

以下是一些常用的替代方案以获得更快的速度,更好的效率:
  • 用sys*系列函数等替代open/seek/tell/<>等标准IO操作
  • 用Socket代替IO::Socket以获得更低开销和内存占用
  • 用get*by*系列函数代替Net::DNS
  • 用index/substr等代替部分低效正则表达式
  • 用select(3参数版本)代替IO::Handle部分功能 .......

    自写低开销模块

    通常我们使用一些Perl模块时,只使用了其中很小一部分的功能,可是却不得不载入整个模块,甚至要载入其他不相关的模块。因此往往使整个程序非常臃肿庞大。

    著名的web管理软件webmin的miniserv(一个简化的http服务端)功能强大,还支持SSL,但资源占用却出奇的少,只有大约5.6MB的大小!这是为什么呢?因为miniserver只使用了2个Perl 系统模块(Socket及POSIX),没有载入其他的模块。一些本需要其他perl 模块的功能,均由web-lib.pl等用系统函数编写代替。

    例如以下是一个获得A记录的高速函数get_mx(),它不依赖任何模块,速度非常快。

    
    sub get_mx {
         my @info = gethostbyname shift;
         my @addr = splice(@info, 4);
         my @rt;
         foreach(@addr) {
              push @rt, join('.', unpack('C4', $_));
          }
         \@rt;
    }
    

    另一个例子,对于标准的IO::Handle对象,可以使用$obj->autoflush(1);来设置缓冲的特性,我们通过使用系统函数select()来获得同样的能力,而无需要载入IO::Handle,代码如下:

    
    sub autoflush {
         my $io = $_[0];
         select((select($io), $|=1)[0]);
    }
    

    使用方法很简单,例如要对IO::Socket::INET类型的$sock设置为立即冲刷,则autoflush($sock)即可。

  • 阅读(2748) | 评论(0) | 转发(0) |
    给主人留下些什么吧!~~