In place copying and duplication

Suppose I have an instance of a nontrivial data structure x, of type mytype that I would like to copy over into a new instance of mytype, y, and I would like to do this in as efficient a manner as possible. If these were arrays of one of the intrinsic types, I would just do:

@. y = x;

Can this operation be overloaded to handle more sophisticated typed? If so, does anyone have an example? Alternatively, is there (or should there be) a copy!/deepycopy! that would perform this action, i.e.,

copy!(y,x);

There is just deepcopy:

y=deepcopy(x)

a deepcopy! doesn’t make sense. Perhaps makes sense :thinking:

You are right, it makes sense:

julia> using BenchmarkTools

julia> a=rand(10000000);

julia> @btime b=deepcopy(a);
  11.126 ms (4 allocations: 76.29 MiB)

julia> @btime begin c=similar(a);c .= a; end;
  11.152 ms (4 allocations: 76.29 MiB)

julia> c=similar(a);

julia> @btime c .= a;
  5.776 ms (2 allocations: 32 bytes)

There is a copy! and copyto!:

julia> @btime copy!(c,a);
  9.624 ms (0 allocations: 0 bytes)

julia> @btime copyto!(c,a);
  5.769 ms (0 allocations: 0 bytes)
julia> @btime unsafe_copyto!(c,1,a,1,length(a));
  5.761 ms (1 allocation: 16 bytes)

julia> l=length(a);

julia> @btime unsafe_copyto!(pointer(c),pointer(a),$l);
  5.768 ms (3 allocations: 48 bytes)

But they don’t work on arbitrary structs.

For an arbitrary mutable struct you could define the method below:

function assign_like_c(dest :: T, src :: T) :: T where {T}
       fields = fieldnames(T)
       setfield!.((dest,), fields, getfield.((src,), fields))
       return dest
end

An example of use:

julia> mutable struct MyType
           a :: Int
           b :: Float64
           c :: String
       end

julia> x = MyType(1, 2.0, "a")
MyType(1, 2.0, "a")

julia> y = MyType(2, 4.0, "aa")
MyType(2, 4.0, "aa")

julia> assign_like_c(x, y)
MyType(2, 4.0, "aa")

julia> x
MyType(2, 4.0, "aa")

Note, however, that if a field of src stores a mutable object, the same mutable object will be referred by dest after, no copy will be done. As you were referring to the .= semantics, I thought this was what you intended.

I think implementing Base.copy! to support your particular type is probably the right thing to do. It’s clear, unambiguous, and can be made as efficient as you want.

Note that y = deepcopy(x) doesn’t actually do what @gideonsimpson is asking for. It creates a new value and then gives that value the y label. It has no effect on whatever value was previously named y.

2 Likes