Perl二维哈希初始化优化:是否存在更快实现方式?
快速初始化Perl二维哈希(值为1)的优化方案
我正在寻找快速的Perl二维哈希初始化方法,已知有推荐用undef的方案,但我需要将哈希值初始化为1。我测试了四种实现方式(命名随意,不用在意),其中for_2d_init_hash_w_array的执行速度最快,但当$limit=4000时,初始化仍需要4-5秒,想知道有没有更快的实现方式。我知道这类场景可以用位向量,但目前希望保持哈希结构。
测试代码
timethese(-2, { for_2d_original => sub { my %hash; my $limit = 100; for(my $i=0; $i<$limit; $i++){ for(my $j=0; $j<$limit; $j++){ $hash{$i}{$j}=1; } } }, for_2d_map => sub { my %hash; my $limit = 100; for(my $i=0; $i<$limit; $i++){ $hash{$i} = {map { $_ => 1 } 0..$limit-1}; } }, for_2d_init_hash => sub { my %hash; my $limit = 100; for(my $i=0; $i<$limit; $i++){ my %tmp_hash; for(my $j=0; $j<$limit; $j++){ $tmp_hash{$j} = 1; } $hash{$i} = \%tmp_hash; } }, for_2d_init_hash_w_array => sub { my %hash; my $limit = 100; my @array = (0..$limit-1); my @init = (1)x$limit; for(my $i=0; $i<$limit; $i++){ my %tmp_hash; @tmp_hash{@array} = @init; $hash{$i} = \%tmp_hash; } }, });
测试结果
- for_2d_original: 3 wallclock secs ( 2.10 usr + 0.00 sys = 2.10 CPU) @ 751.90/s (n=1579)
- for_2d_map: 2 wallclock secs ( 2.18 usr + 0.00 sys = 2.18 CPU) @ 559.17/s (n=1219)
- for_2d_init_hash: 2 wallclock secs ( 2.15 usr + 0.00 sys = 2.15 CPU) @ 994.42/s (n=2138)
- for_2d_init_hash_w_array: 2 wallclock secs ( 2.12 usr + 0.00 sys = 2.12 CPU) @ 1580.19/s (n=3350)
优化方案
方案1:复用子哈希引用(适用于无需修改子哈希的场景)
如果后续不需要单独修改某个子哈希的键值对,所有子哈希结构完全一致,那么可以只创建一次子哈希,让外层哈希的所有键都指向同一个引用。这种方式的速度会有数量级的提升:
for_2d_reuse_ref => sub { my %hash; my $limit = 100; # 仅创建一次子哈希 my $sub_hash = { map { $_ => 1 } 0..$limit-1 }; for(my $i=0; $i<$limit; $i++){ $hash{$i} = $sub_hash; } },
方案2:预先生成子哈希模板再复制(适用于需要独立子哈希的场景)
如果必须保证每个子哈希都是独立的(后续可能修改),可以先通过哈希切片生成一个子哈希模板,之后每次复制这个模板而非重复执行切片操作,能进一步降低开销:
for_2d_pre_subhash => sub { my %hash; my $limit = 100; my @array = 0..$limit-1; my @init = (1) x $limit; # 预先生成子哈希模板 my %sub_hash_template; @sub_hash_template{@array} = @init; for(my $i=0; $i<$limit; $i++){ # 复制模板生成独立子哈希 my %tmp_hash = %sub_hash_template; $hash{$i} = \%tmp_hash; } },
方案3:预生成键值对列表构造哈希
预先生成子哈希所需的全部键值对列表,之后每次直接用列表构造哈希引用,避免重复生成键值对的开销:
for_2d_list_construct => sub { my %hash; my $limit = 100; # 预先生成子哈希的键值对列表 my @kv_pairs = map { $_ => 1 } 0..$limit-1; for(my $i=0; $i<$limit; $i++){ $hash{$i} = { @kv_pairs }; } },
这些方案中,方案1的速度最快,但依赖业务场景是否允许复用引用;方案2和3在需要独立子哈希的情况下,能比原实现进一步提升速度。
内容的提问来源于stack exchange,提问作者Felix
相关产品推荐
相关产品推荐

