diff --git a/Project.toml b/Project.toml index c2d27182..6c6ab1fe 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ArrayInterface" uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "7.28.0" +version = "7.28.1" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" diff --git a/src/ArrayInterface.jl b/src/ArrayInterface.jl index 0ecfec03..08600078 100644 --- a/src/ArrayInterface.jl +++ b/src/ArrayInterface.jl @@ -674,6 +674,14 @@ function restructure(x::Array, y) reshape(convert(Array, y), Base.size(x)...) end +function restructure(x::Array, y::Array) + # When `y` already has `x`'s shape it is its own restructuring. `reshape` is not a no-op + # here: unlike `vec`, it has no same-shape short-circuit and always mints a fresh `Array` + # header (sharing the data), so returning `y` avoids that per-call allocation. + Base.size(x) == Base.size(y) && return y + reshape(convert(Array, y), Base.size(x)...) +end + abstract type AbstractDevice end abstract type AbstractCPU <: AbstractDevice end struct CPUPointer <: AbstractCPU end diff --git a/test/core.jl b/test/core.jl index 3ad091b5..8676f059 100644 --- a/test/core.jl +++ b/test/core.jl @@ -153,6 +153,32 @@ end @test size(yr) == () @test yr == y end + + @testset "same-shape Array fast path" begin + # When `y` already has `x`'s shape, `restructure` returns `y` itself. `reshape` has no + # same-shape short-circuit (unlike `vec`), so it would otherwise mint a fresh Array + # header (sharing the data) on every call. + for dims in ((3,), (2,3), (2,2,2)) + x = rand(dims...) + y = rand(dims...) + @test ArrayInterface.restructure(x, y) === y + end + # ... and does so without allocating + function _restructure_alloc() + x = rand(4); y = rand(4) + ArrayInterface.restructure(x, y) + @allocated ArrayInterface.restructure(x, y) + end + @test _restructure_alloc() == 0 + + # a genuine shape change still reshapes to `x`'s shape (unchanged behavior) + x = rand(2,2) + y = rand(4) + yr = ArrayInterface.restructure(x, y) + @test yr isa Matrix{Float64} + @test size(yr) == (2,2) + @test vec(yr) == y + end end @testset "isstructured" begin