# A limit to free operations

**URL:** <https://discourse.julialang.org/t/a-limit-to-free-operations/87568>\
**Category:** New to Julia\
**Created:** [September 21, 2022, 11:55am UTC](https://discourse.julialang.org/t/a-limit-to-free-operations/87568 "2022-09-21T11:55:56Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![mongi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mongi/32/43334_2.png) [@mongi](https://discourse.julialang.org/u/mongi)\
**Post date:** [September 21, 2022, 11:55am UTC](https://discourse.julialang.org/t/a-limit-to-free-operations/87568/1 "2022-09-21T11:55:56Z")

</div>

Hi,  
I have my own type and implemented (+,-,\*,/) for it “mulT(args…), addT, subT, divT”, and it works without allocations until length(args)\>7 for multiplication and 9 for addition. For each operation there is a cache in the args that receives the result (to avoid allocations).

```julia
    function mulTT(a, b, c, xs...)
      if length(xs)>1
        mulTfoldl( mulTT( mulTT(a,b,xs[end-1],xs[end]) , c,xs[end-1] ,xs[end] ), xs...)
      end
    end

```

```julia
    function mulTfoldl(res, bs...) 
      l = length(bs)
      i = 2; l == i && return res 
      i = 3; l == i && return mulTT(res, bs[1],bs[end-1],bs[end])
      i = 4; l == i && return mulTT(mulTT(res, bs[1],bs[end-1],bs[end]),bs[2],bs[end-1],bs[end])
      i = 5; l == i && return mulTT(mulTT(mulTT(res, bs[1],bs[end-1],bs[end]),bs[2],bs[end-1],bs[end]),bs[3],bs[end-1],bs[end])
      i = 6; l == i && return mulTT(mulTT(mulTT(mulTT(res, bs[1],bs[end-1],bs[end]),bs[2],bs[end-1],bs[end]),bs[3],bs[end-1],bs[end]),bs[4],bs[end-1],bs[end])

```

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [September 21, 2022, 12:13pm UTC](https://discourse.julialang.org/t/a-limit-to-free-operations/87568/2 "2022-09-21T12:13:08Z")

</div>

Is there a specific question, e.g. how to do it for more multiplications?

Just a note, in Julia you could also define new methods for the `+, -, *, /`, e.g.

```julia
struct MyType
   val
end

Base.:(+)( x::MyType, y::MyType ) = MyType( x.val * y.val )

MyType(2) + MyType(2) == MyType(4)

```

---

<div class="post-metadata">

**Author:** ![mongi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mongi/32/43334_2.png) [@mongi](https://discourse.julialang.org/u/mongi)\
**Post date:** [September 21, 2022, 12:17pm UTC](https://discourse.julialang.org/t/a-limit-to-free-operations/87568/3 "2022-09-21T12:17:26Z")

</div>

thank you [SteffenPL](https://discourse.julialang.org/u/SteffenPL) for the quick reply.

```julia
Base.:(+)( x::MyType, y::MyType ) = MyType( x.val * y.val )

```

returns MyType which causes an allocation (the struct contains arrays), so a I am adding a cache to store the result.  
Base.:(+)( x::MyType, y::MyType,z::MyType ) =(+)( z=x \* y return z )…this is why I changed the naming  
It works fine until the number of args \>7  
also I cannot enter the type in the function args when splatting since I am allowing op(number,MyType)  
thanks

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [September 21, 2022, 12:24pm UTC](https://discourse.julialang.org/t/a-limit-to-free-operations/87568/4 "2022-09-21T12:24:11Z")

</div>

> [@mongi](#):
>
> returns MyType which causes an allocation

No, that should not happen. Returning a value doesn’t cause allocations. It does in the example with

```julia
struct MyType
   val
end

```

Because `val` is untyped. If you instead do

```julia
struct MyType
   val::Int
end

```

for example, it doesn’t cause any allocations:

```julia
julia> @allocated MyType(2) + MyType(2)
0

```

---

<div class="post-metadata">

**Author:** ![mongi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mongi/32/43334_2.png) [@mongi](https://discourse.julialang.org/u/mongi)\
**Post date:** [September 21, 2022, 12:28pm UTC](https://discourse.julialang.org/t/a-limit-to-free-operations/87568/5 "2022-09-21T12:28:00Z")

</div>

Sorry [DNF](https://discourse.julialang.org/u/DNF) for not being clear,  
my struct creation involves creating arrays

```julia
struct MyType{T<:Number} 
    vect :: Array{T,1}
    size :: Int
end

```

thanks

---

<div class="post-metadata">

**Author:** ![mongi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mongi/32/43334_2.png) [@mongi](https://discourse.julialang.org/u/mongi)\
**Post date:** [September 21, 2022, 2:17pm UTC](https://discourse.julialang.org/t/a-limit-to-free-operations/87568/6 "2022-09-21T14:17:31Z")

</div>

here is a MWE code. you can copy and test it yourselves.  
It does not allocate. add one argument and it will allocate.  
thank you for your time.

```julia
using BenchmarkTools
import Base: eachindex, getindex, setindex!
struct MyType{T<:Number} 
    coeffs :: Array{T,1}
    size :: Int
end
getindex(a::MyType, n::Int) = a.coeffs[n+1]
setindex!(a::MyType{T}, x::T, n::Int) where {T<:Number} = a.coeffs[n+1] = x
@inline firstindex(a::MyType) = 0
@inline lastindex(a::MyType) = a.size
@inline eachindex(a::MyType) = firstindex(a):lastindex(a)
function mulTfoldl(res::P, bs...) where {P<:MyType}
    l = length(bs)
    i = 2; l == i && return res 
    i = 3; l == i && return mulTT(res, bs[1],bs[end-1],bs[end])
    i = 4; l == i && return mulTT(mulTT(res, bs[1],bs[end-1],bs[end]),bs[2],bs[end-1],bs[end])
    i = 5; l == i && return mulTT(mulTT(mulTT(res, bs[1],bs[end-1],bs[end]),bs[2],bs[end-1],bs[end]),bs[3],bs[end-1],bs[end])
    i = 6; l == i && return mulTT(mulTT(mulTT(mulTT(res, bs[1],bs[end-1],bs[end]),bs[2],bs[end-1],bs[end]),bs[3],bs[end-1],bs[end]),bs[4],bs[end-1],bs[end])
end
function mulTT(a::P, b::R, c::Q, xs...)where {P,Q,R <:Union{MyType,Number}}
    if length(xs)>1
      mulTfoldl( mulTT( mulTT(a,b,xs[end-1],xs[end]) , c,xs[end-1] ,xs[end] ), xs...)
    end
end
function mulTT(a::MyType{T}, b::T,cache1::MyType{T},cache2::MyType{T}) where {T<:Number}
  fill!(cache2.coeffs, b)
  @ __dot__ cache1.coeffs = a.coeffs * cache2.coeffs ##fixed broadcast dimension mismatch
  return cache1
end
mulTT(a::T,b::MyType{T}, cache1::MyType{T},cache2::MyType{T}) where {T<:Number} = mulTT(b , a,cache1,cache2)
function mulTT(a::MyType{T}, b::MyType{T},cache1::MyType{T},cache2::MyType{T}) where {T<:Number}
 for k in eachindex(a)
      @inbounds cache2[k] = a[0] * b[k]
      @inbounds for i = 1:k
        cache2[k] += a[i] * b[k-i]
      end
  end
  @ __dot__ cache1.coeffs = cache2.coeffs
  return cache1
end
function mulTT(a::T, b::T,cache1::MyType{T},cache2::MyType{T}) where {T<:Number}
  cache1[0]=a*b  
  return cache1
end

av = vec(3(rand(1,3) .- 1.5)) 
bv = vec(3(rand(1,3) .- 1.5))
cv = vec(3(rand(1,3) .- 1.5))
dv = vec(3(rand(1,3) .- 1.5))

a0 = MyType(av,2)
b0 = MyType(bv,2)
c0 = MyType(cv,2)
d0 = MyType(dv,2)
e=3rand() - 1.5
f=3rand() - 1.5
cache=[MyType([0.0,0.0,0.0],2),MyType([0.0,0.0,0.0],2)]
function boundaryTest(a0::MyType{Float64},b0::MyType{Float64},c0::MyType{Float64},d0::MyType{Float64},cache::Vector{MyType{Float64}},e::Float64,f::Float64)
    mulTT(e, f, a0, b0, e, c0, cache[1], cache[2])# increasing the args here will allocate
end
@btime boundaryTest(a0,b0,c0,d0,cache,e,f)

```
