批处理之家's Archiver

523066680 发表于 2017-8-4 22:25

Perl 内嵌 C 代码

[i=s] 本帖最后由 523066680 于 2017-8-5 08:23 编辑 [/i]

在优化猜数字游戏脚本的运行效率时(其实是在看Imager模块的示例偶然发现),发现了 Perl 可以内联 C 代码,可以极大地提高效率。
原本跑完5040次猜数字测试,用 perl 写的test函数消耗(累积)可能需要10秒,现在只需要零点几秒。[code]use IO::Handle;
use Inline C;
STDOUT->autoflush(1);

my $nums = "4109";
my $guess = "0124";
my $AB = "00";

for (1..5)
{
        $AB = "00";
        test($nums, $guess, $AB);
        print "$AB\n";
}


__END__
__C__
void test(char *stra, char *strb, char *AB)
{
    int idx;
    for ( idx = 0; idx < 4; idx++ )
    {
        if ( stra[idx] == strb[idx] )
            AB[0]++;
        else
            if ( strchr(stra, strb[idx]) != 0 )
            {
                AB[1]++;
            }
    }
}
[/code]不过以上代码有个问题,就是 $AB = "00" 的重置无效了,每次结果都会被递增,输出如下[code]12
24
36
48
5:[/code]所以C函数部分重写,用临时变量从 '0' 开始计数。(为了统一,我用的是字符'0' )[code]__END__
__C__
void bullcow(char *stra, char *strb, char *AB)
{
    int idx;
    char a = '0';
    char b = '0';

    for ( idx = 0; idx < 4; idx++ )
    {
        if ( stra[idx] == strb[idx] )
            a++;
        else
            if ( strchr(stra, strb[idx]) != 0 )
            {
                b++;
            }
    }

    AB[0] = a;
    AB[1] = b;
}[/code]

codegay 发表于 2017-8-5 01:47

:o
__END__
写在最上面吗?这逻辑我没法懂啊。

523066680 发表于 2017-8-5 08:08

[i=s] 本帖最后由 523066680 于 2017-8-5 14:44 编辑 [/i]

[b]回复 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=201921&ptid=44936]2#[/url] [i]codegay[/i] [/b]

    __END__ 在 perl 里面表示脚本结束的节点,可以放备注、数据之类的信息。

523066680 发表于 2017-8-5 08:57

效率对比

[i=s] 本帖最后由 523066680 于 2017-8-5 09:08 编辑 [/i]

时间占用对比,用 Devel::NYTProf 模块分析效率
perl -d:NYTProf normal.pl
nytprofhtml
[img]http://imgout.ph.126.net/57004002/01.jpg[/img]

perl -d:NYTProf InlineC.pl
nytprofhtml
[img]http://imgout.ph.126.net/57008002/02.jpg[/img]

两份完整的测试代码:[code]use IO::Handle;
STDOUT->autoflush(1);

my $nums = "4109";
my $guess = "0124";
my $AB = "00";

for ( 1 .. 100000 )
{
    $AB = "00";
    bullcow($nums, $guess, \$AB);
    print "$AB\n";
}

sub bullcow
{
    my ($nums, $guess, $AB) = @_;
    my ($A, $B) = (0, 0);
    my $t;

    for my $i ( 0 .. 3 )
    {
        if ( substr($nums, $i, 1) eq substr($guess, $i, 1) )
        {
            $A++;
        }
        else
        {
            $t = substr($guess, $i, 1);
            $B++ if ( $nums =~/$t/ );
        }
    }

    $$AB = "$A$B";
}[/code]内联C的版本:[code]use Inline C;
use IO::Handle;
STDOUT->autoflush(1);

my $nums = "4109";
my $guess = "0124";
my $AB = "00";

for ( 1 .. 100000 )
{
    $AB = "00";
    bullcow($nums, $guess, $AB);
    print "$AB\n";
}

__END__
__C__
void bullcow(char *stra, char *strb, char *AB)
{
    int idx;
    char a = '0';
    char b = '0';

    for ( idx = 0; idx < 4; idx++ )
    {
        if ( stra[idx] == strb[idx] )
            a++;
        else
            if ( strchr(stra, strb[idx]) != 0 )
            {
                b++;
            }
    }

    AB[0] = a;
    AB[1] = b;
}[/code]

codegay 发表于 2017-8-5 14:40

我对perl完全不懂哦。

Wiki 发表于 2018-9-23 00:10

提高效率啊。

页: [1]

Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.