ACEsuit / ACE.jl

Parameterisation of Equivariant Properties of Particle Systems
66 stars 15 forks source link

State allocates #89

Closed cortner closed 2 years ago

cortner commented 2 years ago

How could I have missed this? A struct wrapping a NamedTuple is no longer isbits. Therefore generating a State always allocates. How bizarre.

cortner commented 2 years ago
struct Wrapper{SYMS, TT}
   x::NamedTuple{SYMS, TT}
end

randnt() = Wrapper((a = randn(), b = rand(), c = rand(1:5), 
                   d = randn(SVector{3, Float64})))

X = randnt()
@allocated randnt()  # 64
isbits(X)   # false
isbits(X.x)   # true 
cortner commented 2 years ago

bizarrely,

struct Wrapper1{NT}
   x::NT
end

randnt1() = Wrapper1((a = randn(), b = rand(), c = rand(1:5), 
                   d = randn(SVector{3, Float64})))

X = randnt1()
@allocated randnt1()  # 0
isbits(X)   # true
isbits(X.x)   # true
cortner commented 2 years ago

Looks like this will lead to a solution:

struct Wrapper{NT}
   x::NT 
end 

@generated function syms(w::Wrapper{NamedTuple{SYMS, TT}}
                         ) where {SYMS, TT}
   code = "(" * prod( (":" * string(sym) * ",") for sym in SYMS ) * ")"
   quote
      $(Meta.parse(code))
   end
end

nt = (a = 1, b = 2, c = 5)
w = Wrapper(nt) 

syms(w)
ettersi commented 2 years ago

Isn't randnt and randnt1 exactly the same?

cortner commented 2 years ago

Bad copy-pasting - now corrected.

cortner commented 2 years ago

I'm guessing the problem is related to the fact that symbols are not isbits and the NamedTuple implementation does some deeper magic that I don't have access to, to get around this? But I really don't understand it at all.

ettersi commented 2 years ago

The difference in allocations between randnt() and randnt1() is quite bizarre indeed. My best guess is that this is an artefact of some optimisation heuristic of Julia - sometimes Julia deliberately does not optimise some function calls to avoid excessive compiler overhead for functions which are likely not used very often. But I couldn't come up with a minimal example which would demonstrate this.

cortner commented 2 years ago

Maybe I should file an issue if I don’t get a response in discourse.

cortner commented 2 years ago

But also can it really be an optimisation issue? It’s the fact that Wrapper and a boy isbits that bugs me most

ettersi commented 2 years ago

Fair point, that part can't be an optimisation issue. On the other hand, I observed that Wrapper((a = rand(), b = rand())) allocates while Wrapper((a = 1, b = 1))) doesn't. This looks a lot like Julia optimises in the simplest of cases but chickens out once things get even slightly more complicated.

cortner commented 2 years ago

Very interesting

cortner commented 2 years ago

I've just rewritten the State implementation now; cf. #93