卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章74503本站已运行437

Perl 停滞不前的日子:揭示 C 的隐藏力量

perl 停滞不前的日子:揭示 c 的隐藏力量

有时会发生意想不到的事情,必须与世界分享......这就是这样的情况。

最近,我开始尝试使用 perl 进行数据科学应用程序的工作流管理和低级代码的高级监督。在这种情况下,我为 perl 保留的一个角色是内存缓冲区的生命周期管理,使用 perl 应用程序“分配”内存缓冲区并在用 c、assembly、fortran 编写的计算组件和最好的隐藏宝石之间穿梭。 perl 世界,perl 数据语言。
perl 至少可以通过 3 种方式来分配内存缓冲区:

  1. 生成字节列表并使用 pack 函数将它们转换为字符串。
  2. 使用重复运算符 (x) 生成一个长度等于所需缓冲区大小减一的字符串(在 perl 中,字符串通过空字节终止,因此空字节补偿了我的一个 ).
  3. 通过 inline 或 ffi::platypus 访问外部内存分配器库来分配缓冲区。

以下 perl 代码实现了这三种方法(packstringc 中的 malloc),并允许尝试不同的缓冲区大小、初始值和结果精度(通过对分配的多次迭代进行平均)惯例)

#!/home/chrisarg/perl5/perlbrew/perls/current/bin/perl
use v5.38;
use inline (
    c         => 'data',
    cc        => 'g++',
    ld        => 'g++',
    inc       => q{},      # replace q{} with anything else you need
    ccflagsex => q{},      # replace q{} with anything else you need
    lddlflags => join(
        q{ },
        $config::config{lddlflags},
        q{ },              # replace q{ } with anything else you need
    ),
    libs => join(
        q{ },
        $config::config{libs},
        q{ },              # replace q{ } with anything else you need
    ),
    myextlib => ''
);

use benchmark qw(cmpthese);
use getopt::long;
my ($buffer_size, $init_value, $iterations);
getoptions(
    'buffer_size=i' => $buffer_size,
    'init_value=s'  => $init_value,
    'iterations=i'  => $iterations,
) or die "usage: $0 --buffer_size <size> --init_value <value> --iterations <count>n";
my $init_value_byte = ord($init_value);
my %code_snippets   = (
    'string' =&gt; sub {
        $init_value x ( $buffer_size - 1 );
    },
    'pack' =&gt; sub {
        pack "c*", ( ($init_value_byte) x $buffer_size );
    },
    'c' =&gt; sub {
        allocate_and_initialize_array( $buffer_size, $init_value_byte );
    },
);

cmpthese( $iterations, %code_snippets );

__data__

__c__

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

sv* allocate_and_initialize_array(size_t length, short initial_value) {
    // allocate memory for the array
    char* array = (char*)malloc(length * sizeof(char));
    char initial_value_byte = (char)initial_value;
    if (array == null) {
        fprintf(stderr, "memory allocation failedn");
        exit(1);
    }

    // initialize each element with the initial_value
    memset(array, initial_value_byte, length);
    return newsvuv(ptr2uv(array));
}

</stdint.h></stdlib.h></stdio.h></count></value></size>

将脚本调用为:

./time_mem_alloc.pl -buffer_size=1000000 -init_value=a -iterations=20000

产生了令人惊讶的结果:

          rate   pack      c string
pack     322/s     --   -92%   -99%
c       4008/s  1144%     --   -92%
string 50000/s 15417%  1147%     --

使用 perl string 方法比 c 的性能高出 10 倍

不相信巨大的性能提升,并认为我正在处理 inline::c 中的错误,我用纯 c 重新编码了分配(添加命令行处理/计时等的常用修饰):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

char* allocate_and_initialize_array(size_t length, char initial_value) {
    // allocate memory for the array
    char* array = (char*)malloc(length * sizeof(char));
    if (array == null) {
        fprintf(stderr, "memory allocation failedn");
        exit(1);
    }

    // initialize each element with the initial_value
    memset(array, initial_value, length);

    return array;
}

double time_allocation_and_initialization(size_t length, char initial_value) {
    clock_t start, end;
    double cpu_time_used;

    start = clock();
    char* array = allocate_and_initialize_array(length, initial_value);
    end = clock();

    cpu_time_used = ((double) (end - start)) / clocks_per_sec;
    /* this rudimentary loop prevents the compiler from optimizing out the 
     * allocation/initialization with the de-allocation
    */
    for(size_t i = 1; i  <initial_value>n", argv[0]);
        return 1;
    }

    size_t length = strtoull(argv[1], null, 10);
    char initial_value = argv[2][0];

    double time_taken = time_allocation_and_initialization(length, initial_value);
    printf("time taken to allocate and initialize array: %f secondsn", time_taken);
    printf("initializes per second: %fn", 1/time_taken);

    return 0;
}

/*
compilation command:
gcc -o2 -o time_array_allocation time_array_allocation.c -std=c99

example invocation:
./time_array_allocation 10000000 a
*/
</initial_value></time.h></string.h></stdlib.h></stdio.h>

按照c代码中注释所说的那样调用c程序,
我得到了以下结果:

Time taken to allocate and initialize array: 0.000203 seconds
Initializes per second: 4926.108374

实际上执行的数量级与 inline::c malloc/c 方法的等效分配相同。
在进一步研究这个问题后,我发现我所欣赏的 malloc 牺牲了内存分配的速度以换取通用性,并且有大量更快的内存分配器。看来 perl 正在为其字符串使用这样一个分配器,并在分配缓冲区的任务中击败了 c。

卓越飞翔博客
上一篇: golang框架性能优化如何提升?
下一篇: 返回列表
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏