# Unrolling of loops with operations on CuArrays

**URL:** https://discourse.julialang.org/t/unrolling-of-loops-with-operations-on-cuarrays/70468
**Category:** GPU
**Tags:** gpu, cuarrays, unrolling
**Created:** [October 27, 2021, 10:27am UTC](https://discourse.julialang.org/t/unrolling-of-loops-with-operations-on-cuarrays/70468 "2021-10-27T10:27:15Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![fedoroff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fedoroff/32/53209_2.png) [@fedoroff](https://discourse.julialang.org/u/fedoroff)
#### Post date: [October 27, 2021, 10:27am UTC](https://discourse.julialang.org/t/unrolling-of-loops-with-operations-on-cuarrays/70468/1 "2021-10-27T10:27:15Z")

</div>

I have a loop where at each iteration I do some vectorial operation over CuArray. For example

```julia
using CUDA

N = 4
a = ones(N)

u = CUDA.zeros(1000)

@. u = a[1]
for i=2:N
    @. u += a[i]
end

```

As far as I understand, in this case CUDA creates `N` different kernels for each loop iteration which causes an increased overhead. Are there any ways to unroll the loop to obtain a single CUDA kernel? Like here

```julia
@. u = a[1] + a[2] + a[3] + a[4]

```

---

<div class="post-metadata">

### Author: ![maleadt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maleadt/32/10097_2.png) [@maleadt](https://discourse.julialang.org/u/maleadt)
#### Post date: [October 27, 2021, 3:50pm UTC](https://discourse.julialang.org/t/unrolling-of-loops-with-operations-on-cuarrays/70468/2 "2021-10-27T15:50:38Z")

</div>

> [@fedoroff](#):
>
> Are there any ways to unroll the loop to obtain a single CUDA kernel?

Use `reduce`?

---

<div class="post-metadata">

### Author: ![fedoroff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fedoroff/32/53209_2.png) [@fedoroff](https://discourse.julialang.org/u/fedoroff)
#### Post date: [October 27, 2021, 4:21pm UTC](https://discourse.julialang.org/t/unrolling-of-loops-with-operations-on-cuarrays/70468/3 "2021-10-27T16:21:08Z")

</div>

Do you mean this?

```julia
@. u = reduce(+, a)

```

Then what about the following situation?

```julia
using CUDA

N = 4
a = ones(N)

x = CUDA.ones(1000)
u = CUDA.zeros(1000)

for i=1:N
    @. u += a[i] * x
end

```

---

<div class="post-metadata">

### Author: ![maleadt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maleadt/32/10097_2.png) [@maleadt](https://discourse.julialang.org/u/maleadt)
#### Post date: [October 27, 2021, 4:22pm UTC](https://discourse.julialang.org/t/unrolling-of-loops-with-operations-on-cuarrays/70468/4 "2021-10-27T16:22:03Z")

</div>

`mapreduce`

It’s probably also possible to express this more neatly with Tullio or another indexing notation-package.

---

<div class="post-metadata">

### Author: ![fedoroff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fedoroff/32/53209_2.png) [@fedoroff](https://discourse.julialang.org/u/fedoroff)
#### Post date: [October 27, 2021, 5:10pm UTC](https://discourse.julialang.org/t/unrolling-of-loops-with-operations-on-cuarrays/70468/5 "2021-10-27T17:10:42Z")

</div>

It seems that both mapreduce and Tullio approaches are slower than the original loop:

```julia
using BenchmarkTools
using CUDA
using Tullio

CUDA.allowscalar(false)

function loop(u, a, x)
    @. u = a[1] * x
    for i=2:N
        @. u += a[i]
    end
    return nothing
end

function loop_unrolled(u, a, x)
    @. u = a[1] * x + a[2] * x + a[3] * x + a[4] * x
    return nothing
end

function loop_mapreduce(u, a, x)
    u .= mapreduce(y -> y * x, +, a)
    return nothing
end

function loop_tullio(u, a, x)
    @. u = 0
    @tullio u += a[i] * x
    return nothing
end

N = 4
a = ones(N)
x = CUDA.ones(1000)
u = CUDA.zeros(1000)

@btime CUDA.@sync loop($u, $a, $x)
@btime CUDA.@sync loop_unrolled($u, $a, $x)
@btime CUDA.@sync loop_mapreduce($u, $a, $x)
@btime CUDA.@sync loop_tullio($u, $a, $x)

```

```julia
  21.863 μs (105 allocations: 5.73 KiB)
  10.349 μs (7 allocations: 496 bytes)
  42.210 μs (219 allocations: 12.31 KiB)
  69.036 μs (242 allocations: 25.36 KiB)

```
