使用Views进行内存分配:Julia切片性能优化疑问
Julia切片与@views性能优化问题
测试代码
using Distributions using BenchmarkTools u = randn(100, 2) res = ones(100) idx = 1 u_vector = u[:, idx] @btime $res = $1.0 .- $u_vector; @btime $res = $1.0 .- $u[:,idx]; @btime @views $res = $1.0 .- $u[:,idx];
运行结果
julia> @btime $res = $1.0 .- $u_vector; 37.478 ns (1 allocation: 896 bytes) julia> @btime $res = $1.0 .- $u[:,idx]; 607.383 ns (13 allocations: 1.97 KiB) julia> @btime @views $res = $1.0 .- $u[:,idx]; 397.597 ns (6 allocations: 1.08 KiB)
问题分析与优化方案
为什么@views版本性能不如第一条?
@views确实避免了切片时创建副本,生成的是SubArray视图,但广播操作对SubArray的处理效率远低于原生Vector:
SubArray需要额外的索引映射计算,无法像原生数组那样直接连续访问内存;- Julia的广播优化逻辑对原生数组更友好,针对
SubArray的广播会产生额外开销; - 原代码三条语句都是重新赋值res,即生成新数组再覆盖res,
SubArray的广播生成新数组的过程本身就比原生Vector慢。
优化方案(无额外内存分配)
既然res已经预先分配好内存,应该原地修改res的元素,而非重新生成数组赋值,这样能彻底消除内存分配,同时适配动态变化的idx:
方案1:利用广播原地赋值
@btime @. res = 1.0 - @views u[:, idx];
方案2:手动循环(适配更复杂计算场景)
@btime begin @inbounds for i in eachindex(res) res[i] = 1.0 - u[i, idx] end end
两种写法都能实现0内存分配,性能接近第一条原生Vector的水平。
内容的提问来源于stack exchange,提问作者user1691278
相关产品推荐
相关产品推荐

