有时会发生意想不到的事情,必须与世界分享......这就是这样的情况。
最近,我开始尝试使用 perl 进行数据科学应用程序的工作流管理和低级代码的高级监督。在这种情况下,我为 perl 保留的一个角色是内存缓冲区的生命周期管理,使用 perl 应用程序“分配”内存缓冲区并在用 c、assembly、fortran 编写的计算组件和最好的隐藏宝石之间穿梭。 perl 世界,perl 数据语言。
perl 至少可以通过 3 种方式来分配内存缓冲区:
- 生成字节列表并使用 pack 函数将它们转换为字符串。
- 使用重复运算符 (x) 生成一个长度等于所需缓冲区大小减一的字符串(在 perl 中,字符串通过空字节终止,因此空字节补偿了我的一个 ).
- 通过 inline 或 ffi::platypus 访问外部内存分配器库来分配缓冲区。
以下 perl 代码实现了这三种方法(pack、string 和 c 中的 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' => sub { $init_value x ( $buffer_size - 1 ); }, 'pack' => sub { pack "c*", ( ($init_value_byte) x $buffer_size ); }, 'c' => 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 failed\n"); 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 failed\n"); 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 seconds\n", time_taken); printf("initializes per second: %f\n", 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。
以上就是Perl 停滞不前的日子:揭示 C 的隐藏力量的详细内容,更多请关注知识资源分享宝库其它相关文章!
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。