# Macro to sum over short loop

**URL:** https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574
**Category:** New to Julia
**Tags:** macros, runtimegeneratedfunc, unrolling
**Created:** [July 5, 2020, 9:49pm UTC](https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574 "2020-07-05T21:49:15Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)
#### Post date: [July 5, 2020, 9:49pm UTC](https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574/1 "2020-07-05T21:49:15Z")

</div>

I find myself writing code like this repeatedly

```julia
function ke(I::CartesianIndex{d},u::Array{Float64}) where d
   s = 0.
   for i in 1:d
      s+=u[I,i]^2
   end
   return 0.5s
end

```

and wanted to give Julia’s macros a try. I was hoping to only need to write something like this

```julia
ke(I::CartesianIndex{d},u::Array{Float64}) where d = 0.5@sum(d, i -> u[I,i]^2)

```

I tried a few things, but I can’t get the dimension `d` to work generally and when I got it sort of working (hard coding the dimension), the resulting code is only half as fast as the for-loop. Is there some way to just generate the for-loop sum automatically?

Note that I’m not using the Base `sum` function

```julia
ke(I::CartesianIndex{d},u::Array{Float64}) where d = 0.5sum(i -> u[I,i]^2,1:d)

```

since it is around 10 times slower than the for loop.

---

<div class="post-metadata">

### Author: ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)
#### Post date: [July 5, 2020, 10:36pm UTC](https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574/2 "2020-07-05T22:36:49Z")

</div>

Would `Base.sum` on a generator expression work for you?

```julia
ke(I::CartesianIndex{D}, u::Array{Float64}) where {D} = 0.5sum(u[I,i]^2 for i in 1:D)

```

I did not perform extensive benchmarks, but at first sight it looks like this is as fast as your original version

---

<div class="post-metadata">

### Author: ![dpsanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dpsanders/32/3573_2.png) [@dpsanders](https://discourse.julialang.org/u/dpsanders)
#### Post date: [July 5, 2020, 10:37pm UTC](https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574/3 "2020-07-05T22:37:28Z")

</div>

I think you can just do

```julia
0.5 * sum(x -> x^2, @view u[I, :])

```

---

<div class="post-metadata">

### Author: ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)
#### Post date: [July 5, 2020, 10:52pm UTC](https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574/4 "2020-07-05T22:52:42Z")

</div>

Also note that you’ve forgotten to elide bound checks. The two versions should probably look like this:

```julia
function ke1(I::CartesianIndex{d},u::Array{Float64}) where d
   s = 0.
   @inbounds for i in 1:d
      s+=u[I,i]^2
   end
   return 0.5s
end

ke2(I::CartesianIndex{D}, u::Array{Float64}) where {D} =
    0.5sum(@inbounds(u[I,i]^2) for i in 1:D)

```

---

<div class="post-metadata">

### Author: ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)
#### Post date: [July 5, 2020, 11:13pm UTC](https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574/5 "2020-07-05T23:13:22Z")

</div>

That’s neat, but most of the functions are more complex than this, with multiple arrays and adjustments made to `I`. Also, I just wanted to try out macros…

---

<div class="post-metadata">

### Author: ![WschW](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wschw/32/6575_2.png) [@WschW](https://discourse.julialang.org/u/WschW)
#### Post date: [July 6, 2020, 12:21am UTC](https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574/6 "2020-07-06T00:21:49Z")

</div>

The reason that `d` is not working generally is because the value of d is determine at type inference time which occurs after macro expand time. The way to get around this is with generated functions:

[https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions-1](https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions-1)

---

<div class="post-metadata">

### Author: ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)
#### Post date: [July 6, 2020, 7:33am UTC](https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574/7 "2020-07-06T07:33:20Z")

</div>

This works and is exactly as fast as the for-loop. Why is this generator so much faster than the `sum(fun,1:d)`?

---

<div class="post-metadata">

### Author: ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)
#### Post date: [July 6, 2020, 7:34am UTC](https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574/8 "2020-07-06T07:34:20Z")

</div>

Sorry for over-simplifying. I have those in the real code.

---

<div class="post-metadata">

### Author: ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)
#### Post date: [July 6, 2020, 8:40am UTC](https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574/9 "2020-07-06T08:40:20Z")

</div>

It looks like there still remains a function call in `sum(fun, 1:d)`. I’m not sure why everything doesn’t get inlined.

If you define your own simple higher-order summation function (in much the same way as your original implementation, simply abstracting away the body of the loop in a function argument), you can also make everything work fast:

```julia
function simple_sum(fun, range)
    s = 0.
    for i in range
        s += fun(i)
    end
    s
end

ke3(I::CartesianIndex{D}, u) where {D} =
    0.5simple_sum(i->@inbounds(u[I,i]^2), 1:D)

```

  

In any case, I think higher-order functions are the way to go here, not macros. The performance of these relies, much like in your original implementation, on the compiler being able to unroll (small) loops when the range is known at compile time (i.e. extracted from a type parameter and constant-propagated to the for loop itself). And I would say that it is one of the best features of Julia that method specialization and constant propagation combine so well with optimization techniques featured by LLVM, so that we can have the unrolled loops without having to explicitly resort to metaprogramming techniques.

Now, you are right that, for the sake of learning metaprogramming techniques, unrolling loops is an interesting use case. (I think the first experiments I did with macros in Julia were along those lines too). For the sake of the example, here are a few things one might say about this in your particular case.

With macros, you would not rely on the compiler unrolling the loop. Instead, you’d directly generate a series of loop body blocks (one for each iteration of the loop). Again, this is probably not the best way to go here, but if you do need macros in other similar cases, try and take a look at the macros defined in [Base.Cartesian](https://docs.julialang.org/en/v1/devdocs/cartesian/#dev-cartesian-reference-1): `@ntuple`, `@nexprs` and their friends can be very powerful. A very simple example would be:

```julia
julia> @macroexpand Base.Cartesian.@nexprs 2 i -> s += u[I, i]
quote
    s += u[I, 1]
    s += u[I, 2]
end

```

But, as already said by @WschW, you’d need the dimension parameter to be known at macro expansion time. If you still want to extract this parameter from a parameterized type, then you could use [`@generated` functions](https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions-1), so that your original implementation could be rewritten with an explicitly unrolled loop like so:

```julia
@generated function ke4(I::CartesianIndex{D}, u) where {D}
    quote
        s = 0.
        Base.Cartesian.@nexprs $D i -> s += @inbounds(u[I, i])
        0.5s
    end
end

```

This could be combined, like before, with a higher-order function that helps you abstract away the loop body:

```julia
@generated function unrolled_sum(fun, ::Val{D}) where {D}
    quote
        s = 0.
        Base.Cartesian.@nexprs $D i -> s += fun(i)
    end
end

ke5(I::CartesianIndex{D}, u) where {D} =
    0.5unrolled_sum(i->@inbounds(u[I,i]^2), Val(D))

```

---

<div class="post-metadata">

### Author: ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)
#### Post date: [July 6, 2020, 9:23am UTC](https://discourse.julialang.org/t/macro-to-sum-over-short-loop/42574/10 "2020-07-06T09:23:23Z")

</div>

Thanks. I appreciate the walkthrough even though it doesn’t seem to be needed for this case.
