struct Node
x::Float64
# more fields
end
nodes = Vector{Node}(undef, bignumber)
And I have a vector of Elements that each contain some nodes
struct Element
numNodes::UInt8
tag::Int
nodes::Vector{Node}
# more fields
end
elems = [Element(n, i, nodes[inds]) for inds in indices]
This is terrible for size reasons, so my first thought is to use Ref since the manual makes it seem like the Julia version of pointers.
struct Element
numNodes::UInt8
tag::Int
nodes::Vector{Ref{Node}}
# more fields
end
elems = [Element(n, i, Ref.(nodes[inds])) for inds in indices]
But this doesn’t work, the only thing that seems to work is using SubArrays/Views
struct Element
numNodes::UInt8
tag::Int
nodes::SubArray
# more fields
end
elems = [Element(4,i,@view nodes[inds] #= etc =#) for inds in indices]
My question is then two fold:
Is this the correct way of doing this or is there a better more performant way to do and think about this in Julia?
How do you make a struct that contains a reference to another bit of memory? Since using a view would seem silly for something that isn’t an array.
P.S. Is a Ref not actually the Julia version of a Pointer?
Hi there!
Can you give a complete MWE, and explain why the solutions you have “don’t work” or are “terrible”? In particular, it seems indices is a vector of vectors?
julia> struct Foo
y::Float64
end
julia> struct Bar
l::Int
o::Vector{Ref{Foo}}
end
julia> foos = [Foo(i) for i in 1:4]
4-element Vector{Foo}:
Foo(1.0)
Foo(2.0)
Foo(3.0)
Foo(4.0)
julia> bar = Bar(2, Ref.(foos[1:2]))
Bar(2, Ref{Foo}[Base.RefValue{Foo}(Foo(1.0)), Base.RefValue{Foo}(Foo(2.0))])
julia> foos[1] = Foo(5)
Foo(5.0)
julia> bar
Bar(2, Ref{Foo}[Base.RefValue{Foo}(Foo(1.0)), Base.RefValue{Foo}(Foo(2.0))])
# Foo(1.0) has not changed
The reason
struct Element
numNodes::UInt8
tag::Int
nodes::Vector{Node}
# more fields
end
is terrible is because in the case that you have many Elements referencing the same nodes you are copying the data of those nodes, thus this is a waste of memory.
Each Element could reference an arbitrary subset of nodes which is why indices is a vector of the indices of the nodes that each Element has.
My goal is to minimize memory usage since I could be making hundreds of thousands of Nodes, each of which must belong to at least one Element.
From reading the manual it seems like one should be able to use Ref to reference another piece of memory like pointers do in C, am I mistaken?
I apologize for not giving a more thorough explanation.
Using views does make sense, though the context in how I’ve seen views used is very different from how I’ve seen pointers used on other languages (i.e. a way to avoid copying, rather than a data type in and of themselves).
And I was wondering if there was a convention I was missing.
My main point of confusion was how different the syntax is when pointing to an array vs not an array (as shown by @fatteneder ). And I didn’t know that broadcasting made copies of the arguments.
Not only broadcasting makes a copy.
In the example above already slicing the array with foos[1:2] makes a copy. And then calling Ref.(foos[1:2]) makes a second copy.
Just one more question:
Why V<:AbstractVector{<:Foo} and not V<:SubArray?
Do these tell the compiler substantiality different things or is this a human readability choice?
There is almost never a performance benefit to tightening constraints on type parameters (unless maybe the constraint is a concrete type, but then it should not be a parameter). They are mostly just there for for their ability to add convenient guardrails against stuff going too crazy. They should mostly be unconstrained or constrained to broad, abstract types.
In your case, maybe there’s some world where a NTuple{3,Foo} would be reasonable in that parameter. Though this is not <:AbstractVector{Foo}, it meets most of the interface for it and would probably work for most things. There’s no cost to leaving this as a hypothetical possibility, so it’s nice to have it.
At runtime, you will have a Bar{SubArray{<complete type parameterization to make a concrete type>)}}.This is what the compiler will actually specialize for. Since it specializes at that level, there isn’t real benefit to a tighter a-priori constraint.
But drop dim as a field in the struct. It should just be a type parameter. If I’m not mistaken, the dim field is now unconnected with the parameter, and also wholly redundant.