# Performance of Comprehensions versus For Loops

**URL:** <https://discourse.julialang.org/t/performance-of-comprehensions-versus-for-loops/30066>\
**Category:** New to Julia\
**Created:** [October 18, 2019, 10:03pm UTC](https://discourse.julialang.org/t/performance-of-comprehensions-versus-for-loops/30066 "2019-10-18T22:03:20Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![jbu](https://avatars.discourse-cdn.com/v4/letter/j/a8b319/32.png) [@jbu](https://discourse.julialang.org/u/jbu)\
**Post date:** [October 18, 2019, 10:03pm UTC](https://discourse.julialang.org/t/performance-of-comprehensions-versus-for-loops/30066/1 "2019-10-18T22:03:20Z")

</div>

Is there any significant performance difference between comprehensions and equivalent `for` loops for constructing arrays? Similarly, is there any reason to prefer one over the other in terms of performance? The [documentation](https://docs.julialang.org/en/v1/manual/arrays/#Comprehensions-1) doesn’t seem to discuss this.

(I’m referring to speed of code execution rather than how visually appealing or easy to type one or the other is.)

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [October 18, 2019, 10:18pm UTC](https://discourse.julialang.org/t/performance-of-comprehensions-versus-for-loops/30066/2 "2019-10-18T22:18:50Z")

</div>

There shouldn’t be much of a difference if the compiler has access to the same amount of information in both cases. You can easily benchmark two simple cases to find out.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [October 18, 2019, 10:47pm UTC](https://discourse.julialang.org/t/performance-of-comprehensions-versus-for-loops/30066/3 "2019-10-18T22:47:15Z")

</div>

Broadly speaking, it’s easier for a new user to get good performance for simple tasks with a comprehension than with a for loop because of gotchas like bounds checking and inefficient indexing strategies. However, for complicated (usually nested) problems, there are often things you can do with for loops that are ‘smarter’ than the code a comprehension would produce.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [October 19, 2019, 6:39am UTC](https://discourse.julialang.org/t/performance-of-comprehensions-versus-for-loops/30066/4 "2019-10-19T06:39:56Z")

</div>

> [@Mason](#):
>
> because of gotchas like bounds checking and inefficient indexing strategies

My major difficulty in writing generic code with `for` loops (or any preallocated container which I fill) is figuring out the result type. Comprehensions, `map` and broadcasting employ an elaborate machinery for this which is optimized for the compiler, but it is quite hard to replicate is user code for more involved types.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [October 19, 2019, 3:19pm UTC](https://discourse.julialang.org/t/performance-of-comprehensions-versus-for-loops/30066/5 "2019-10-19T15:19:13Z")

</div>

`push!!` from [BangBang.jl](https://github.com/tkf/BangBang.jl) will allow you to widen the eltype of a container as needed using a similar strategy to map, broadcast and comprehensions.

There was some discussion about this on Slack recently; it’d be nice if `Base` defined a generic, interface for widening an `eltype` on demand like this. This is a pretty complicated wheel that many people have had to independently reinvent out there in the package ecosystem.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [October 20, 2019, 5:08am UTC](https://discourse.julialang.org/t/performance-of-comprehensions-versus-for-loops/30066/6 "2019-10-20T05:08:54Z")

</div>

I am aware of it, but many extra ingredients are needed to replicate what eg `map` or `collect` does. Eg recursively calling on widening:

> [@Functional implementation of collect](https://discourse.julialang.org/t/functional-implementation-of-collect/15177):
>
> I am working on something that is in a sense a generalization of collect (except it would pick named tuples apart into vectors, apply some simple RLE compression, etc), and I wanted to understand the type widening part better. For my problem, I do not know the element type or the size ex ante, so the following type is defined to make results comparable (all the code is available in a [gist](https://gist.github.com/tpapp/f0cfb7fceaee5823c1afc0a1f4019ca9) for quick copy&paste): # define an iterator with unknown type and length struct MyItr N::Int end Base.Itera…

using size hints for preallocating, etc. It is all feasible (since it is done in `Base` 😉), but you essentially end up reimplementing `Base` functionality, which is very involved.

---

<div class="post-metadata">

**Author:** ![jbu](https://avatars.discourse-cdn.com/v4/letter/j/a8b319/32.png) [@jbu](https://discourse.julialang.org/u/jbu)\
**Post date:** [October 22, 2019, 10:03pm UTC](https://discourse.julialang.org/t/performance-of-comprehensions-versus-for-loops/30066/7 "2019-10-22T22:03:30Z")

</div>

Thanks all, this information is really helpful. @Tamas_Papp just to make sure I understand, an advantage of comprehensions then is that they automatically figure out what data type the resulting array will be composed of? So the user doesn’t need to figure out the right data type and preallocate manually?

As a follow-up question, in general will a program be faster if you do happen to know the resulting data type beforehand and do the array preallocation by hand? Just curious for future reference.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [October 23, 2019, 5:45am UTC](https://discourse.julialang.org/t/performance-of-comprehensions-versus-for-loops/30066/8 "2019-10-23T05:45:36Z")

</div>

Yes, that’s what I consider the main advantage of comprehensions, `map` (which is currently implemented using comprehensions, eg `collect`), and broadcasting.

Calculating result types can be tedious and sometimes impossible (a lot of Julia code punts on this, even in `Base` or the standard libraries, leading to subtle bugs). IMO the best approach is a flexible heuristic, used to implement `collect`, which can be described as making an educated guess (eg using the first element) and then widening if necessary. For type stable code, the compiler is smart enough to figure out that widening is not needed and that branch is never called.

That said, if you _can_ figure out the result type and **reuse** preallocated containers, you can avoid the cost of allocation. This is a viable strategy for speeding up some inner loops. OTOH, if you just preallocate every time, there is usually no significant advantage (provided that eg `collect` can figure out the correct size using the same mechanism).

These are all trade-offs you have to think about in the context of your problem. A lot of code, especially libraries for AD, works best with a non-modifying, “functional” approach. So I generally try to write code using this approach and then maybe preallocate in some tight loops that profiling shows to be significant. In my experience, the gains from using smarter algorithms always outweighs the cost of allocation most of the time, so I generally worry little about this unless there is a compelling reason.
