Skip to content

Should we make reshape(::Array, …) return a ReshapedArray? #24237

@mbauman

Description

@mbauman

When we introduced ReshapedArrays, we deferred making the breaking change where reshape(::Array, …) returns a ReshapedArray. I say we change that for 1.0.

There are two reasons to do this:

  • Semantics. The wrapper makes it much more obvious that the data are shared (see, e.g., https://stackoverflow.com/questions/38777469/function-for-reshape-view), and the semantics are much more obviously "just" an indexing transformation into the parent. It seems like folks are getting used to all the array wrappers.
  • Performance. We'll eventually add more optimizations for array wrappers, allowing them all to live on the stack (right?). ReshapedArray would ride along "for free" here, whereas enabling the same optimization for the C array header would be extra work. (I'm not in a position to estimate how much work — all I know is that it's work that few would be able to do.) This would put is in the unfortunate situation where ReshapedArray(::Array, …) would be completely allocation-free whereas reshape(::Array, …) would not be. Either way, cursory benchmarks on 0.6 show the Julian ReshapedArray to already be faster than the C reshape on construction, and the two look to be comparable on access.

While breaking, I think it's only marginally so. Folks who expect f(reshape(A, 2,3,4)) to hit f(A::Array) would have to widen their signatures, and similarly for struct fields. Is there anything else?

Benchmarks are on 0.6 just because it was so much easier to do so with BenchmarkTools right now. We'll definitely need the Nanosoldier back on its feet for this one.

  | | |_| | | | (_| |  |  Version 0.6.0 (2017-06-19 13:05 UTC)
 _/ |\__'_|_|_|\__'_|  |  Official http://julialang.org/ release
|__/                   |  x86_64-apple-darwin13.4.0

julia> using BenchmarkTools
WARNING: Compat.UTF8String is deprecated, use String instead.
  likely near /Users/mbauman/.julia/v0.6/JLD/src/JLD.jl:971

julia> # Base._reshape, but without the ::Array override
       function reshape2(parent::AbstractArray, dims::Dims)
           n = Base._length(parent)
           prod(dims) == n || throw(DimensionMismatch("parent has $n elements, which is incompatible with size $dims"))
           Base.__reshape((parent, IndexStyle(parent)), dims)
       end
reshape2 (generic function with 1 method)

julia> A = rand(60);

julia> C = @btime reshape($A, (5,4,3));
  48.178 ns (2 allocations: 112 bytes)

julia> J = @btime reshape2($A, (5,4,3));
  19.855 ns (1 allocation: 48 bytes)

julia> @btime sum($C);
  30.455 ns (0 allocations: 0 bytes)

julia> @btime sum($J);
  30.113 ns (0 allocations: 0 bytes)

julia> A = rand(50, 4, 3);

julia> C = @btime reshape($A, (100, 6));
  45.914 ns (2 allocations: 96 bytes)

julia> J = @btime reshape2($A, (100, 6));
  17.994 ns (1 allocation: 32 bytes)

julia> @btime sum($C)
  172.636 ns (0 allocations: 0 bytes)
290.51809277377106

julia> @btime sum($J)
  172.978 ns (0 allocations: 0 bytes)
290.51809277377106

Metadata

Metadata

Assignees

No one assigned

    Labels

    arrays[a, r, r, a, y, s]needs decisionA decision on this change is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions