Dramatic performance change by adding additional unused method

BTW, this thread might be relevant.

I tried with having a Vector of abstract types. Like this:

abstract type AbstractPlacement{T<:AbstractFloat} end

#---Volume-------------------------------------------------------------------------
struct Volume{T<:AbstractFloat, S<:AbstractShape{T}, M<:AbstractMaterial}
    label::String
    shape::S                            # Reference to the actual shape
    material::M                         # Reference to material
    daughters::Vector{AbstractPlacement{T}}
end

#---PlacedVolume-------------------------------------------------------------------
struct PlacedVolume{T<:AbstractFloat,S<:AbstractShape{T}, M<:AbstractMaterial} <: AbstractPlacement{T}
    transformation::Transformation3D{T}
    volume::Volume{T,S,M}
end

Volume{T}(label::String, shape::S, material::M) where {T,S,M} = Volume{T,S,M}(label, shape, material, Vector{AbstractPlacement{T}}())

The performance is twice worst than what I had originally.

Are you sure you’re not trying to express too much? Most of the time the compiler can figure out the types. Can’t you just use a type parameter, and leave it to the compiler to find out what it is? Both a homogeneous and a heterogeneous vector can be expressed as Vector{T} for some T.

1 Like

Sorry, perhaps I am not expressing myself well. In the system I developing I would be ending with about 20 different ‘shapes’ with more of less deep hierarchies. As I said, I can either have a unique Volume that encapsulates a polymorphic shape and then have a homogenous vector of all the daughter volumes (this was the original design). Or, have a different Volume type for each shape and have an heterogenous vector of daughters. I do not see how I can completely avoid polymorphism.

How heavy would be to change the max_methods from 3 to something like 20?

What I’m saying is that with this type specification

it is impossible to get the benefits when the vector genuinely is homogeneous. You are forcing the element type to always be abstract, even when it’s not necessary. If you instead use

daughters::Vector{T} # with T<: AbstractPlacement 

you allow a concrete eltype when possible, and an abstract type when necessary.

yes, but each eltype is different in general. A ‘mother’ volume may have ‘boxes’, ‘tubes’, ‘spheres’, etc. as daughters.

Should work anyway. What am I missing?

Vector{T} is a vector of ‘anything’, including arbitrarily nested types, type unions, or abstract types.

1 Like

Is this the typocalypse?

1 Like

For this kind of low-level polymorphism I would suggest moving the typing from the Julia type system to your own data. In other words, I suggest you create a single type that has as many Float64 “point” fields as you may need for your largest shape, and a single tag field which the type is an @enum of your 20 primitive shapes. Then write a single area function that has a long if ...; ...; elseif ...; ...; end that deals with your 20 possible cases. This is not easily extensible by third parties but will give you some idea of how fast your system could possible be.

2 Likes

Unityper, which I linked to before, tries to provide a more convenient API for this, and tries to compactify the final struct in the case where not all their fields are the same.

But you can also do it manually.
I would strongly recommend something like this approach.

2 Likes

Sorry if I am very slow to understand. I see that Vector{T} can be a vector of anything. But what I need is a vector that the elements are of different types all of them inheriting from some Abstract type.
In languages such as C++ this will be implemented with an abstract class (with pure visual methods). I start understanding that the Abstract type in Julia is not equivalent (as it is not used to define an API) and is only used as a pre-condition for argument checking.

Thanks. But honestly I was thinking that Julia would provide better help for this. This is equivalent of C or even lower, since there is no switch statement in Julia.
I was expecting that multi-dispatch would help me. I do have a reference to a AbstractShape and the proper method is called for each of the shapes. What I cannot understand is why the compiler cannot infer what are return values, even if I specify them in the signature of the function. The other thing I do not understand is why there is memory allocation involved in the dispatching.

Yes, and Vector{T} can express that. My point is that parameterizing like that leaves room either for a totally heterogeneous vector or for a homogeneous vector of concrete eltype.