# Allocation and slow down when # of types involved increase (slower than C++ virtual methods)

**URL:** <https://discourse.julialang.org/t/allocation-and-slow-down-when-of-types-involved-increase-slower-than-c-virtual-methods/87656>\
**Category:** Performance\
**Tags:** question, hep\
**Created:** [September 22, 2022, 3:43pm UTC](https://discourse.julialang.org/t/allocation-and-slow-down-when-of-types-involved-increase-slower-than-c-virtual-methods/87656 "2022-09-22T15:43:34Z")\
**Posts on this page:** 1\
**Showing post:** 58

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [November 8, 2022, 11:32am UTC](https://discourse.julialang.org/t/allocation-and-slow-down-when-of-types-involved-increase-slower-than-c-virtual-methods/87656/58 "2022-11-08T11:32:53Z")

</div>

@rdeits unfortunately [rdeits/ConcreteInterfaces.jl](https://github.com/rdeits/ConcreteInterfaces.jl/blob/master/demo.ipynb) is not working with a recent version of Julia. In any case, I have made a test with `FunctionWrapper` by hand instead of using the `@interface` macro, which looks very handy.

I have defined a partial interface to `Shapes` with only two methods (inside and extent) to start with.

```julia
#---Define shape interfaces (aka C++ virtual table)------------------------------------------------
struct ShapeInterface{T<:AbstractFloat} <: AbstractInterface
    self::AbstractShape{T}
    inside::FunctionWrapper{Int64, Tuple{Point3{T}}}
    extent::FunctionWrapper{Tuple{Point3{T},Point3{T}},Tuple{}}
    ShapeInterface{T}(self::AbstractShape{T}) where T = new(self, (p::Point3{T} -> inside(self, p)), (()->extent(self)))
end
import Geom4hep: inside, extent
inside(i::ShapeInterface{T}, p::Point3{T}) = i.inside(p)
extent(i::ShapeInterface{T}) = i.extent()

```

then I define the new `BooleanShape` like this:

```julia
struct NBooleanShape{T<:AbstractFloat, OP} <: AbstractShape{T}
    left::ShapeInterface{T} # the mother (or left) volume A in unplaced form
    right::ShapeInterface{T} # (or right) volume B in placed form, acting on A with a boolean operation
    transformation::Transformation3D{T} # placement of "right" with respect of "left" 
end

```

The NBooleanShape has good properties (type stability, no allocations at runtime, etc.) but the performance is not better. With a nested boolean shape you get a factor 2 worst performance.

```julia
with the original code (as in the head of repository)
  62.709 ns (0 allocations: 0 bytes)
with the new BooleanShape
  110.988 ns (0 allocations: 0 bytes)

```

> **full example**
>
> ```julia
> using Revise
> using Geom4hep
> 
> using FunctionWrappers
> import FunctionWrappers: FunctionWrapper
> abstract type AbstractInterface end
> 
> #---Define shape interfaces (aka C++ virtual table)------------------------------------------------
> struct ShapeInterface{T<:AbstractFloat} <: AbstractInterface
> self::AbstractShape{T}
> inside::FunctionWrapper{Int64, Tuple{Point3{T}}}
> extent::FunctionWrapper{Tuple{Point3{T},Point3{T}},Tuple{}}
> ShapeInterface{T}(self::AbstractShape{T}) where T = new(self, (p::Point3{T} -> inside(self, p)), (()->extent(self)))
> end
> 
> import Geom4hep: inside, extent
> inside(i::ShapeInterface{T}, p::Point3{T}) = i.inside(p)
> extent(i::ShapeInterface{T}) = i.extent()
> 
> #---New definition of Boolean Shapes--------------------------------------------------------------------
> struct NBooleanShape{T<:AbstractFloat, OP} <: AbstractShape{T}
> left::ShapeInterface{T} # the mother (or left) volume A in unplaced form
> right::ShapeInterface{T} # (or right) volume B in placed form, acting on A with a boolean operation
> transformation::Transformation3D{T} # placement of "right" with respect of "left" 
> end
> const NBooleanUnion{T} = NBooleanShape{T, :union}
> const NBooleanSubtraction{T} = NBooleanShape{T, :subtraction}
> const NBooleanIntersection{T} = NBooleanShape{T, :intersection}
> 
> #---Constructor--------------------------------------------------------------------------------------
> function NBooleanSubtraction(left::AbstractShape{T}, right::AbstractShape{T}, place::Transformation3D{T}=one(Transformation3D{T})) where T<:AbstractFloat
> NBooleanSubtraction{T}(ShapeInterface{T}(left), ShapeInterface{T}(right), place)
> end
> 
> function inside(shape::NBooleanSubtraction{T}, point::Point3{T})::Int64
> (; left, right, transformation) = shape
> lpoint = transformation * point
> positionA = inside(left, point)
> positionA == kOutside && return kOutside
> positionB = inside(right, lpoint)
> if positionA == kInside && positionB == kOutside
> return kInside;
> elseif positionA == kInside && positionB == kSurface ||
> positionB == kOutside && positionA == kSurface
> return kSurface
> else
> return kOutside
> end
> end
> 
> function extent(shape::NBooleanSubtraction{T})::Tuple{Point3{T},Point3{T}} where {T}
> extent(shape.left)
> end
> 
> const T = Float64
> #---New boolean instance
> nboolean = NBooleanSubtraction(NBooleanSubtraction(NBooleanSubtraction(NBooleanSubtraction(Box{T}(2.035, 100.35000000000001, 0.5750000000000001),
> Box{T}(0.3175, 100.35000000000001, 0.065),
> Transformation3D{T}(1.7175000000000002, 0.0, 0.51)),
> Box{T}(0.3175, 100.35000000000001, 0.065),
> Transformation3D{T}(1.7175000000000002, 0.0, -0.51)),
> Box{Float64}(0.3175, 100.35000000000001, 0.065),
> Transformation3D{T}(-1.7175000000000002, 0.0, 0.51)),
> Box{Float64}(0.3175, 100.35000000000001, 0.065),
> Transformation3D{T}(-1.7175000000000002, 0.0, -0.51))
> #---Old boolean instance
> boolean = BooleanSubtraction(BooleanSubtraction(BooleanSubtraction(BooleanSubtraction(Box{T}(2.035, 100.35000000000001, 0.5750000000000001),
> Box{T}(0.3175, 100.35000000000001, 0.065),
> Transformation3D{T}(1.7175000000000002, 0.0, 0.51)),
> Box{T}(0.3175, 100.35000000000001, 0.065),
> Transformation3D{T}(1.7175000000000002, 0.0, -0.51)),
> Box{Float64}(0.3175, 100.35000000000001, 0.065),
> Transformation3D{T}(-1.7175000000000002, 0.0, 0.51)),
> Box{Float64}(0.3175, 100.35000000000001, 0.065),
> Transformation3D{T}(-1.7175000000000002, 0.0, -0.51))
> 
> point = Point3{T}(0,0,0)
> 
> # benchmaking                             
> using BenchmarkTools
> @btime inside(boolean, point)
> @btime inside(nboolean, point)
> 
> ```

---

_[View the full topic](https://discourse.julialang.org/t/allocation-and-slow-down-when-of-types-involved-increase-slower-than-c-virtual-methods/87656)._
