Skip to content

Commit

Permalink
copyto! timing
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffFessler committed Jul 10, 2021
1 parent 7d9b31c commit 91bdc6d
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 22 deletions.
21 changes: 11 additions & 10 deletions archive/src/ndgrid-repeat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ lazy and non-lazy versions of ndgrid

using LazyArrays: Applied, ApplyArray

export ndgrid_array, ndgrid_lazy
export ndgrid_array_r, ndgrid_lazy

"""
grid = ndgrid_repeat(v::AbstractVector, dims::Dims{D}, d::Int)
Expand All @@ -24,14 +24,14 @@ end


"""
ndgrid_array(args::AbstractVector...)
ndgrid_array_r(args::AbstractVector...)
Returns tuple of `length(args)` arrays, each of size `tuple(length.(args)...)`.
This method is provided for convenience and testing,
but `ndgrid` is less efficient than broadcast so should be avoided.
The tuple returned here requires `prod(length.(args)) * length(args)` memory;
using `ndgrid_lazy` is an alternative that uses `O(length(args))` memory.
"""
function ndgrid_array(args::AbstractVector...)
function ndgrid_array_r(args::AbstractVector...)
D = length(args)
T = Tuple{ntuple(i -> Array{eltype(args[i]), D}, D)...} # return type
fun = i -> ndgrid_repeat(args[i], length.(args), i)
Expand All @@ -41,17 +41,18 @@ end

# lazy array method for a single element of the ndgrid tuple:

const Repeat = Applied{A, typeof(ndgrid_repeat), Tuple{V,Dim,Int}} where
const _Repeat = Applied{A, typeof(ndgrid_repeat), Tuple{V,Dim,Int}} where
{A <: Any, V <: AbstractVector, Dim <: Dims{D} where D}
Base.ndims(r::Repeat) = length(r.args[2])
Base.size(r::Repeat) = r.args[2]
Base.eltype(r::Repeat) = eltype(r.args[1])
Base.ndims(r::_Repeat) = length(r.args[2])
Base.size(r::_Repeat) = r.args[2]
Base.eltype(r::_Repeat) = eltype(r.args[1])

const RepeatA{T,N} = ApplyArray{T, N, typeof(ndgrid_repeat)}
# Base.IndexStyle(RepeatA) = IndexCartesian() # default
const Repeat{T,N} = ApplyArray{T, N, typeof(ndgrid_repeat)}
# Base.IndexStyle(Repeat) = IndexCartesian() # default

#@inline # todo
Base.@propagate_inbounds function Base.getindex(
r::RepeatA{T,N},
r::Repeat{T,N},
i::Vararg{Int,N},
) where {T,N}
@boundscheck checkbounds(r, i...)
Expand Down
40 changes: 28 additions & 12 deletions archive/test/ndgrid.jl → archive/test/ndgrid-repeat.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# ndgrid.jl
# ndgrid-repeat.jl
# test lazy and non-lazy version

using LazyGrids: ndgrid, ndgrid_lazy, ndgrid_repeat
#using LazyGrids: ndgrid, ndgrid_array
using LazyGrids: btime
include("../src/ndgrid-repeat.jl") # ndgrid_array_r, ndgrid_lazy, ndgrid_repeat

using Test: @test, @testset, @test_throws, @inferred

@testset "ndgrid" begin
@testset "ndgrid_array_r" begin

# easy test with vectors of the same type
xg, yg, zg = @inferred ndgrid(1:4, 5:7, 8:9)
xg, yg, zg = @inferred ndgrid_array_r(1:4, 5:7, 8:9)
@test xg == repeat(1:4, 1, 3, 2)
@test yg == repeat((5:7)', 4, 1, 2)

Expand All @@ -17,34 +19,31 @@ using Test: @test, @testset, @test_throws, @inferred
y = [1//2, 3//4]
z = [:a, :b, :c]

@inferred ndgrid(x, y) # compiler figures out type,
@inferred ndgrid(x, z) # surprisingly

@test_throws BoundsError ndgrid_repeat(x, (4,2,3), 0)
@test_throws DimensionMismatch ndgrid_repeat(x, (4,2,3), 2)

@inferred ndgrid_repeat(x, (4,2,3), 1)
@inferred ndgrid_repeat(y, (4,2,3), 2)
@inferred ndgrid_repeat(z, (4,2,3), 3)
# xg, yg, zg = @inferred ndgrid(x, y, z) # @inferred fails here
xg, yg, zg = ndgrid(x, y, z)
xg, yg, zg = @inferred ndgrid_array_r(x, y, z)
@test zg == repeat(reshape(z, (1,1,3)), 4, 2, 1)
end


@testset "ndgrid_lazy" begin
x = 1:4
y = [1//2, 3//4]
z = [:a, :b, :c]
a = ndgrid(x, y)
a = ndgrid_array_r(x, y)
b = @inferred ndgrid_lazy(x, y)
@test a == b
a = ndgrid(x, y, z)
a = ndgrid_array_r(x, y, z)
# b = @inferred ndgrid_lazy(x, y, z) # @inferred fails here
b = ndgrid_lazy(x, y, z)
@test a == b

# indexing
(xn, _, _) = ndgrid(x, y, z)
(xn, _, _) = ndgrid_array_r(x, y, z)
(xl, _, _) = ndgrid_lazy(x, y, z)

@test xn == xl
Expand All @@ -58,6 +57,23 @@ end
@test sizeof(xl) < 100 # small!
end


# copyto! test using LazyArrays version for comparison
using BenchmarkTools: @benchmark

dims = (2^7,2^8,2^9)
x,y,z = map(rand, dims)
(xl, _, _) = ndgrid_lazy(map(Base.OneTo, dims)...)
xa = Array(xl)
out = zeros(eltype(x), dims)
sizeof.((xl, xa))

ta = @benchmark copyto!(out, xa) # 15.9ms
@show btime(ta)

tl = @benchmark copyto!(out, xl) # 179.9ms with lots of memory
@show btime(tl)

#=
@which getindex(xn, 3) # calls Base
@which getindex(xl, 1, 1, 1) # calls specialized getindex
Expand Down
71 changes: 71 additions & 0 deletions docs/lit/examples/2-copyto.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#---------------------------------------------------------
# # [copyto! test](@id 2-copyto)
#---------------------------------------------------------

# This page examines `copyto!` speed of the lazy grids in the Julia package
# [`LazyGrids`](https://github.com/JuliaArrays/LazyGrids.jl).

# ### Setup

# Packages needed here.

using LazyGrids: ndgrid, ndgrid_array
using LazyGrids: btime, @timeo # not exported; just for timing tests here
using BenchmarkTools: @benchmark


# ### Overview

# There are 4 sub-types of `AbstractGrids`.
# Here we focus on the simplest (using `OneTo`)
# and most general (`AbstractVector`).


# #### `OneTo`

dims = (2^7,2^8,2^9)

(xl, _, _) = ndgrid(dims...) # lazy version
xa = Array(xl) # regular dense Array
out = zeros(Int, dims)
sizeof.((xl, xa))


#
ta = @benchmark copyto!(out, xa) # 12.6ms
btime(ta)

#
tl = @benchmark copyto!(out, xl) # 27.3ms
btime(tl)


# #### `AbstractVector`

x,y,z = map(rand, dims)

(xl, _, _) = ndgrid(dims...)
xa = Array(xl)
out = zeros(eltype(x), dims)
sizeof.((xl, xa))


#
ta = @benchmark copyto!(out, xa) # 15.7ms
btime(ta)

#
tl = @benchmark copyto!(out, xl) # 21.7ms
btime(tl)


# These results suggest that `copyto!` is somewhat slower
# for a lazy grid than for an `Array`.
# This drawback could be reduced or possibly even eliminated
# by adding a dedicated `copyto!` method
# for lazy grids.
# Submit an issue or PR if there is a use case
# that needs faster `copyto!`.
#
# See
# [broadcasting](https://docs.julialang.org/en/v1/manual/interfaces/#man-interfaces-broadcasting).

0 comments on commit 91bdc6d

Please sign in to comment.