拼接生成器内容与另一向量的最优性能实现方案
生成带固定后缀的数组:性能优化方案
我需要生成一个数组,要求末尾包含固定值集合(例如3,2,1),期望实现的效果如下:
julia> N = 10; julia> [(i^2 for i in 1:N)..., 3, 2, 1] 13-element Vector{Int64}: 1 4 9 16 25 36 49 64 81 100 3 2 1
已知当N(或后缀长度)很大时,用vcat(即;语法)构建数组通常比展开(splatting,...操作)更高效,比如下面的性能对比:
julia> let a = fill(0.0, 1000) @btime [$a..., $10.0] end; 34.250 μs (1002 allocations: 23.64 KiB) julia> let a = fill(0.0, 1000) @btime [$a; $10.0] end; 685.833 ns (18 allocations: 8.36 KiB)
那么展开生成器是否存在性能损耗?有没有更优的替代方案?先看展开生成器的基准测试结果:
julia> @btime [(i^2 for i in 1:$1000)..., 3, 2, 1]; 52.541 μs (2477 allocations: 93.95 KiB)
如果直接用vcat语法拼接生成器和后缀值,会得到不符合预期的结果——因为生成器不是数组,会被直接作为单个元素放入数组:
julia> [(i^2 for i in 1:10); 3; 2; 1] 4-element Vector{Any}: Base.Generator{UnitRange{Int64}, var"#47#48"}(var"#47#48"(), 1:10) 3 2 1
优化方案
核心解决思路是先将生成器转换为数组,再通过vcat拼接后缀元素,有两种高效实现方式:
- 使用数组推导式生成前半部分,再拼接后缀数组
@btime vcat([i^2 for i in 1:$1000], [3,2,1]);
- 用
collect显式转换生成器,再拼接后缀
@btime vcat(collect(i^2 for i in 1:$1000), [3,2,1]);
这两种方式的性能远优于展开生成器:它们会预先计算总元素数量(1000+3=1003),一次性分配足够的内存,避免了展开操作中频繁的数组扩容和内存分配,大幅减少分配次数和耗时。
性能对比
以N=1000为例,优化后的方案耗时通常在1μs左右,分配次数仅20次以内,相比展开生成器的50+μs和2000+次分配,性能提升非常明显。
内容的提问来源于stack exchange,提问作者NHDaly
相关产品推荐
相关产品推荐

