julia> include("test.jl")test_all (generic function with 1 method)
julia> test_all(5)test_strideelapsed time: 1.596122539 seconds (0 bytes allocated)test_viewelapsed time: 9.502840329 seconds (94208000 bytes allocated, 0.38% gc time)test_unsafeelapsed time: 1.683452985 seconds (16303000 bytes allocated)using ArrayViewsimport Base: size, getindex, setindex!, ndimstype UnsafeSlice{T,N, P<:Ptr} <: AbstractArray p::P start::Int stride::Int size::NTuple{N,Int}endsize(s::UnsafeSlice) = s.sizesize(s::UnsafeSlice, i::Int) = s.size[i]ndims{T,N}(s::UnsafeSlice{T,N}) = Ngetindex(s::UnsafeSlice, i::Int) = unsafe_load(s.p, s.start+(i-1)*s.stride)setindex!(s::UnsafeSlice, x, i::Int) = unsafe_store!(s.p, x, s.start+(i-1)*s.stride)
function UnsafeSlice(a, slicedim::Int, start=1) p = pointer(a) str = stride(a, slicedim) UnsafeSlice{eltype(a), ndims(a), typeof(p)}(p, start, str, size(a))end
function update(a::UnsafeSlice, idx, off) for i=1:size(a, idx) a[i] = -10*off+i end aendfunction update(a::AbstractArray, idx, off) for i=1:size(a, idx) a[i] = -10*off+i end aendfunction update(a::Array, idx, off, stride) for i=1:size(a, idx) a[off+(i-1)*stride] = -10*off+i end aendfunction setk_UnSafe{T}(a::Array{T,3}) us = UnsafeSlice(a, 3) for j=1:size(a,2),i=1:size(a,1) us.start = (j-1)*size(a,1)+i #off = sub2ind(size(a), i, j, 1) update(us, 3, us.start) end aendfunction setk_View{T}(a::Array{T,3}) for j=1:size(a,2),i=1:size(a,1) off = sub2ind(size(a), i, j, 1) update(view(a, i, j, :), 3, off) end aendfunction setk_Stride{T}(a::Array{T,3}) str = stride(a, 3) for j=1:size(a,2),i=1:size(a,1) off = (j-1)*size(a,1)+i update(a, 3, off, str) end aend
function test_stride(n) a = zeros(Int, (320, 320, 320)) # warmup setk_Stride(a); @time (for i=1:n; setk_Stride(a); end)endfunction test_view(n) a = zeros(Int, (320, 320, 320)) # warmup setk_View(a); @time (for i=1:n; setk_View(a); end)endfunction test_unsafe(n) a = zeros(Int, (320, 320, 320)) # warmup setk_UnSafe(a); @time (for i=1:n; setk_UnSafe(a); end)end
function test_all(n) println("test_stride") test_stride(n) println("test_view") test_view(n) println("test_unsafe") test_unsafe(n)end
function setk_unsafeview{T}(a::Array{T,3}) for j=1:size(a,2),i=1:size(a,1) off = sub2ind(size(a), i, j, 1) update(unsafe_view(a, i, j, :), 3, off) end aendjulia> test_all(5);test_strideelapsed time: 2.156173128 seconds (0 bytes allocated)test_viewelapsed time: 9.30964534 seconds (94208000 bytes allocated, 0.47% gc time)test_unsafeelapsed time: 2.169307471 seconds (16303000 bytes allocated)test_unsafeviewelapsed time: 8.955876793 seconds (90112000 bytes allocated, 0.41% gc time)julia> test_all(5);test_strideelapsed time: 2.159909265 seconds (0 bytes allocated)test_viewelapsed time: 9.029025282 seconds (94208000 bytes allocated, 0.43% gc time)test_unsafeelapsed time: 2.621667854 seconds (114606240 bytes allocated, 2.41% gc time)test_unsafeviewelapsed time: 8.888434466 seconds (90112000 bytes allocated, 0.44% gc time)$ julia --track-allocation=all _ _ _ _(_)_ | A fresh approach to technical computing (_) | (_) (_) | Documentation: http://docs.julialang.org _ _ _| |_ __ _ | Type "help()" for help. | | | | | | |/ _` | | | | |_| | | | (_| | | Version 0.3.8-pre+13 (2015-04-17 18:08 UTC) _/ |\__'_|_|_|\__'_| | Commit 0df962d* (2 days old release-0.3)|__/ | x86_64-redhat-linux
julia> include("test.jl")test_all (generic function with 1 method)
julia> test_unsafe(5) - function update(a::AbstractArray, idx, off) 8151120 for i=1:320 #size(a, idx) 0 a[i] = -10*off+i - end 0 a - end -
- function setk_UnSafe{T}(a::Array{T,3}) 760 us = UnsafeSlice(a, 3) 0 for j=1:size(a,2),i=1:size(a,1) 8151120 us.start = (j-1)*320+i #size(a,1)+i - #off = sub2ind(size(a), i, j, 1) 0 update(us, 3, us.start) - end 0 a - end
- function test_unsafe(n) 0 a = zeros(Int, (320, 320, 320)) - # warmup 0 setk_UnSafe(a); 0 clear_malloc_data() - #@time ( 0 for i=1:n; setk_UnSafe(a); end
- end
Ran my script in 0.4 and got these results...
julia> test_all(5)
test_stride
elapsed time: 2.008043041 seconds (0 bytes allocated)
test_view
elapsed time: 8.871387399 seconds (42 MB allocated, 0.23% gc time in 2 pauses with 1 full sweep)
test_unsafe
elapsed time: 2.308598574 seconds (46 MB allocated, 0.68% gc time in 2 pauses with 1 full sweep)
test_unsafeview
elapsed time: 9.106651158 seconds (0 bytes allocated)
julia> test_all(10)
test_stride
elapsed time: 4.012240175 seconds (0 bytes allocated)
test_view
elapsed time: 18.085514211 seconds (85 MB allocated, 0.16% gc time in 4 pauses with 1 full sweep)
test_unsafe
elapsed time: 4.477773618 seconds (93 MB allocated, 1.12% gc time in 4 pauses with 1 full sweep)
test_unsafeview
elapsed time: 18.146163969 seconds (0 bytes allocated)
So the allocation for the new unsafeview has been reduced to zero but it has become slower than the regular view.
Perhaps the compiler optimizations that have been discussed here are occuring since the only occurence of 'unsafeview' is the argument to a function.
$ ~/gitrepos/julia0.4/julia --track-allocation=user --inline=no
_
_ _ _(_)_ | A fresh approach to technical computing
(_) | (_) (_) | Documentation: http://docs.julialang.org
_ _ _| |_ __ _ | Type "help()" for help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 0.4.0-dev+4385 (2015-04-20 14:52 UTC)
_/ |\__'_|_|_|\__'_| | Commit 5499882 (0 days old master)
|__/ | x86_64-redhat-linux
julia> include("test_alloc.jl")
test_unsafe (generic function with 2 methods)
julia> test_unsafe(1);
julia>
[ptb@cyrus julia]$ - using ArrayViews
- import Base: size, getindex, setindex!, ndims, start, stride, pointer
-
- type UnsafeSlice{T,N, P<:Ptr} <: AbstractArray
- start::Int
- stride::Int
- size::NTuple{N,Int}
- p::P
- end
-
- size(s::UnsafeSlice) = size(s.size)
-
7356448 size(s::UnsafeSlice, i::Int) = s.size[i]
-
- ndims{T,N}(s::UnsafeSlice{T,N}) = N
-
- getindex(s::UnsafeSlice, i::Int) = unsafe_load(s.p, s.start+(i-1)*s.stride)
-
1048559648 setindex!(s::UnsafeSlice, x, i::Int) = unsafe_store!(s.p, x, s.start+(i-1)*s.stride)
-
- function UnsafeSlice(a, slicedim::Int, start=1)
0 p = pointer(a)
-
0 str = stride(a, slicedim)
-
368 UnsafeSlice{eltype(a), ndims(a), typeof(p)}(start, str, size(a),p)
- end
-
- function update(a::UnsafeSlice, idx, off)
-
0 for i=1:size(a, idx)
-
0 a[i] = -10*off+i
- end
-
0 a
- end
-
- function setk_UnSafe{T}(a::Array{T,3})
0 us = UnsafeSlice(a, 3)
-
0 for j=1:size(a,2),i=1:size(a,1)
-
14712896 us.start = sub2ind(size(a), i, j, 1)
-
0 update(us, 3, us.start)
- end
-
0 a
- end
-
- function test_unsafe(n, time=true)
0 a = zeros(Int, (320, 320, 320))
-
- # warmup
0 setk_UnSafe(a);
-
0 Profile.clear_malloc_data()
-
0 for i=1:n
-
0 setk_UnSafe(a)
-
- end
-
0 a
- end
- - function test_unsafe(n, time=true)
0 a = zeros(Int, (320, 320, 320))
-
- # warmup
0 setk_UnSafe(a);
-
0 Profile.clear_malloc_data()
-
0 for i=1:n
-
0 setk_UnSafe(a)
-
- end
-
0 a
- endjulia> @code_warntype setindex!(UnsafeSlice(zeros(Int, (10, 10, 10)), 3), -10, 1)Variables: s::UnsafeSlice{Int64,3,Ptr{Int64}} x::Int64 i::Int64
Body: begin # /usr/local/runs/compact-fd/symbolic/julia/test_alloc.jl, line 19: GenSym(1) = (top(getfield))(s::UnsafeSlice{Int64,3,Ptr{Int64}},:p)::Ptr{Int64} GenSym(0) = (top(box))(Int64,(top(add_int))((top(getfield))(s::UnsafeSlice{Int64,3,Ptr{Int64}},:start)::Int64,(top(box))(Int64,(top(mul_int))((top(box))(Int64,(top(sub_int))(i::Int64,1)),(top(getfield))(s::UnsafeSlice{Int64,3,Ptr{Int64}},:stride)::Int64)))) return (top(pointerset))(GenSym(1),x::Int64,GenSym(0))::Ptr{Int64} end::Ptr{Int64}
julia> @code_warntype size(UnsafeSlice(zeros(Int, (10, 10, 10)), 3), 3)Variables: s::UnsafeSlice{Int64,3,Ptr{Int64}} i::Int64
Body: begin # /usr/local/runs/compact-fd/symbolic/julia/test_alloc.jl, line 13: return getfield((top(getfield))(s::UnsafeSlice{Int64,3,Ptr{Int64}},:size)::Tuple{Int64,Int64,Int64},i::Int64)::Int64 end::Int64
julia> @code_warntype update(UnsafeSlice(zeros(Int, (10, 10, 10)), 3), 3, 20)Variables: a::UnsafeSlice{Int64,3,Ptr{Int64}} idx::Int64 off::Int64 #s3::Int64 i::Int64
Body: begin # /usr/local/runs/compact-fd/symbolic/julia/test_alloc.jl, line 31: GenSym(3) = getfield((top(getfield))(a::UnsafeSlice{Int64,3,Ptr{Int64}},:size)::Tuple{Int64,Int64,Int64},idx::Int64)::Int64 GenSym(0) = $(Expr(:new, UnitRange{Int64}, 1, :(((top(getfield))(Intrinsics,:select_value))((top(sle_int))(1,GenSym(3))::Bool,GenSym(3),(top(box))(Int64,(top(sub_int))(1,1)))::Int64))) #s3 = (top(getfield))(GenSym(0),:start)::Int64 unless (top(box))(Bool,(top(not_int))(#s3::Int64 === (top(box))(Int64,(top(add_int))((top(getfield))(GenSym(0),:stop)::Int64,1))::Bool)) goto 1 2: GenSym(6) = #s3::Int64 GenSym(7) = (top(box))(Int64,(top(add_int))(#s3::Int64,1)) i = GenSym(6) #s3 = GenSym(7) # line 33: GenSym(2) = (top(box))(Int64,(top(add_int))((top(box))(Int64,(top(mul_int))(-10,off::Int64)),i::Int64)) GenSym(5) = (top(getfield))(a::UnsafeSlice{Int64,3,Ptr{Int64}},:p)::Ptr{Int64} GenSym(4) = (top(box))(Int64,(top(add_int))((top(getfield))(a::UnsafeSlice{Int64,3,Ptr{Int64}},:start)::Int64,(top(box))(Int64,(top(mul_int))((top(box))(Int64,(top(sub_int))(i::Int64,1)),(top(getfield))(a::UnsafeSlice{Int64,3,Ptr{Int64}},:stride)::Int64)))) (top(pointerset))(GenSym(5),GenSym(2),GenSym(4))::Ptr{Int64} 3: unless (top(box))(Bool,(top(not_int))((top(box))(Bool,(top(not_int))(#s3::Int64 === (top(box))(Int64,(top(add_int))((top(getfield))(GenSym(0),:stop)::Int64,1))::Bool)))) goto 2 1: 0: # line 36: return a::UnsafeSlice{Int64,3,Ptr{Int64}} end::UnsafeSlice{Int64,3,Ptr{Int64}}.
julia> @code_warntype setk_UnSafe(zeros(Int, (10, 10, 10)))
Variables:
a::Array{Int64,3}
us::UnsafeSlice{T,N,P<:Ptr{T}}
#s1::Int64
j::Int64
#s3::Int64
i::Int64
####p#3255#3279::Ptr{Int64}
####str#3256#3280::Int64
Body:
begin # /usr/local/runs/compact-fd/symbolic/julia/test_alloc.jl, line 40:
####p#3255#3279 = (top(ccall))(:jl_array_ptr,$(Expr(:call1, :(top(apply_type)), :Ptr, Int64)),$(Expr(:call1, :(top(svec)), :Any)),a::Array{Int64,3},0)::Ptr{Int64}
####str#3256#3280 = stride(a::Array{Int64,3},3)::Int64
us = ((top(apply_type))(UnsafeSlice,Int64,3,typeof(####p#3255#3279::Ptr{Int64})::Type{Ptr{Int64}})::Type{_<:UnsafeSlice{Int64,N,Ptr{Int64}}})(1,####str#3256#3280::Int64,(top(tuple))((top(arraysize))(a::Array{Int64,3},1)::Int64,(top(arraysize))(a::Array{Int64,3},2)::Int64,(top(arraysize))(a::Array{Int64,3},3)::Int64)::Tuple{Int64,Int64,Int64},####p#3255#3279::Ptr{Int64})::UnsafeSlice{T,N,P<:Ptr{T}} # line 42:
GenSym(4) = (top(arraysize))(a::Array{Int64,3},2)::Int64
GenSym(0) = $(Expr(:new, UnitRange{Int64}, 1, :(((top(getfield))(Intrinsics,:select_value))((top(sle_int))(1,GenSym(4))::Bool,GenSym(4),(top(box))(Int64,(top(sub_int))(1,1)))::Int64)))
#s1 = (top(getfield))(GenSym(0),:start)::Int64
unless (top(box))(Bool,(top(not_int))(#s1::Int64 === (top(box))(Int64,(top(add_int))((top(getfield))(GenSym(0),:stop)::Int64,1))::Bool)) goto 1
2:
GenSym(7) = #s1::Int64
GenSym(8) = (top(box))(Int64,(top(add_int))(#s1::Int64,1))
j = GenSym(7)
#s1 = GenSym(8)
GenSym(5) = (top(arraysize))(a::Array{Int64,3},1)::Int64
GenSym(2) = $(Expr(:new, UnitRange{Int64}, 1, :(((top(getfield))(Intrinsics,:select_value))((top(sle_int))(1,GenSym(5))::Bool,GenSym(5),(top(box))(Int64,(top(sub_int))(1,1)))::Int64)))
#s3 = (top(getfield))(GenSym(2),:start)::Int64
unless (top(box))(Bool,(top(not_int))(#s3::Int64 === (top(box))(Int64,(top(add_int))((top(getfield))(GenSym(2),:stop)::Int64,1))::Bool)) goto 4
5:
GenSym(9) = #s3::Int64
GenSym(10) = (top(box))(Int64,(top(add_int))(#s3::Int64,1))
i = GenSym(9)
#s3 = GenSym(10) # line 44:
GenSym(11) = (top(arraysize))(a::Array{Int64,3},1)::Int64
GenSym(12) = (top(arraysize))(a::Array{Int64,3},2)::Int64
GenSym(13) = (top(arraysize))(a::Array{Int64,3},3)::Int64
(top(setfield!))(us::UnsafeSlice{T,N,P<:Ptr{T}},:start,(top(convert))((top(fieldtype))((top(typeof))(us::UnsafeSlice{T,N,P<:Ptr{T}})::Type{_<:UnsafeSlice{T,N,P<:Ptr{T}}},:start)::Type{_},(top(box))(Int64,(top(add_int))(i::Int64,(top(box))(Int64,(top(mul_int))(GenSym(11),(top(box))(Int64,(top(add_int))((top(box))(Int64,(top(sub_int))(j::Int64,1)),(top(box))(Int64,(top(mul_int))(GenSym(12),(top(box))(Int64,(top(sub_int))(1,1)))))))))))::Any)::Any # line 46:
update(us::UnsafeSlice{T,N,P<:Ptr{T}},3,(top(getfield))(us::UnsafeSlice{T,N,P<:Ptr{T}},:start)::Int64)::UnsafeSlice{T,N,P<:Ptr{T}}
6:
unless (top(box))(Bool,(top(not_int))((top(box))(Bool,(top(not_int))(#s3::Int64 === (top(box))(Int64,(top(add_int))((top(getfield))(GenSym(2),:stop)::Int64,1))::Bool)))) goto 5
4:
3:
unless (top(box))(Bool,(top(not_int))((top(box))(Bool,(top(not_int))(#s1::Int64 === (top(box))(Int64,(top(add_int))((top(getfield))(GenSym(0),:stop)::Int64,1))::Bool)))) goto 2
1:
0: # line 49:
return a::Array{Int64,3}
end::Array{Int64,3}
using ArrayViews
import Base: size, getindex, setindex!, ndims, start, stride, pointer
type UnsafeSlice{T,N, P<:Ptr} <: AbstractArray{T,N}
start::Int
stride::Int
size::NTuple{N,Int}
p::P
end
size(s::UnsafeSlice) = s.size
size(s::UnsafeSlice, i::Int) = s.size[i]
ndims{T,N}(s::UnsafeSlice{T,N}) = N
getindex(s::UnsafeSlice, i::Int) = unsafe_load(s.p, s.start+(i-1)*s.stride)
setindex!(s::UnsafeSlice, x, i::Int) = unsafe_store!(s.p, x, s.start+(i-1)*s.stride)
function UnsafeSlice(a, slicedim::Int, start=1)
p = pointer(a)
str = stride(a, slicedim)
UnsafeSlice{eltype(a), ndims(a), typeof(p)}(start, str, size(a),p)
end
function update(a::UnsafeSlice, idx, off)
for i=1:size(a, idx)
a[i] = -10*off+i
end
a
end
function setk_UnSafe{T}(a::Array{T,3})
us = UnsafeSlice(a, 3)
for j=1:size(a,2),i=1:size(a,1)
us.start = sub2ind(size(a), i, j, 1)
update(us, 3, us.start)
end
a
end
function test_unsafe(n, time=true)
a = zeros(Int, (320, 320, 320))
# warmup
setk_UnSafe(a);
Profile.clear_malloc_data()
for i=1:n
setk_UnSafe(a)
end
a
end