# When to use broadcasting with . vs map

**URL:** <https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078>\
**Category:** General Usage\
**Tags:** broadcast\
**Created:** [March 27, 2021, 2:05pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078 "2021-03-27T14:05:43Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![SL6Trip](https://avatars.discourse-cdn.com/v4/letter/s/4da419/32.png) [@SL6Trip](https://discourse.julialang.org/u/SL6Trip)\
**Post date:** [March 27, 2021, 2:05pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/1 "2021-03-27T14:05:43Z")

</div>

If I want to apply a function across say a `Vector` in julia, there are two ways I could do it:

```julia
map(f, seq)

f.(seq)

```

Is any way “better” than the other? In fact, is there any difference in the implementation between using map and broadcasting with . ?

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [March 27, 2021, 2:15pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/2 "2021-03-27T14:15:09Z")

</div>

On a user level, I prefer to use `map` when `f` is somehow complicated. It’s usually either when you need to write it as an anonymous function, or when you can’t use syntax sugar and have to call methods explicitly, or when function is so complicated that you want to use `do` syntax.

Example 1:

```julia
f(x, y) = x + y
v = [1, 2, 3]

map(x -> f(x, 1), v)
# vs
(x -> f(x, 1)).(v)

```

Example 2

```julia
v = [(1, 1), (2, 2), (3, 3)]

map(x -> x[2], v)
# vs
getindex.(v, 2)

```

Example 3:

```julia
v = [(0, 1), (1, 2), (2, 3)]

map(v) do x
   println(v[1])
   v[2]*v[2]
end

```

In all other cases, broadcasting is usually easier to read and use.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 27, 2021, 2:19pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/3 "2021-03-27T14:19:42Z")

</div>

> [@SL6Trip](#):
>
> Is any way “better” than the other? In fact, is there any difference in the implementation between using map and broadcasting with . ?

`map` (or a comprehension) is probably faster if `seq` is a generic iterable collection rather than an array, since broadcasting first calls `collect` to convert iterables into arrays. For example:

```julia
julia> @btime sqrt.(i^2 for i in 1:100);
  325.522 ns (2 allocations: 1.75 KiB)

julia> @btime map(sqrt, i^2 for i in 1:100);
  235.057 ns (1 allocation: 896 bytes)

```

Notice that the memory allocation is doubled in the broadcast case, because of the extra array allocated by `collect`.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [March 27, 2021, 2:27pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/4 "2021-03-27T14:27:34Z")

</div>

In example-1, wouldn’t broadcasting look simpler this way:

```julia
julia> f.(v,1)
3-element Array{Int64,1}:
 2
 3
 4

```

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [March 27, 2021, 2:29pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/5 "2021-03-27T14:29:48Z")

</div>

Yeah, you are right, not the best illustration. What I was trying to say, there can be situations, when you have to write some sort of anonymous function. Maybe better example is hand-written implementation of the `sign` function

```julia
v = [-10, 0, 10]
map(x -> x > 0 ? 1 : x < 0 ? -1 : 0, v)

```

---

<div class="post-metadata">

**Author:** ![SL6Trip](https://avatars.discourse-cdn.com/v4/letter/s/4da419/32.png) [@SL6Trip](https://discourse.julialang.org/u/SL6Trip)\
**Post date:** [March 27, 2021, 2:55pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/6 "2021-03-27T14:55:20Z")

</div>

So broadcasting will `.` actually convert `seq` to an array, then eventually create and return something of the type that `seq` originally was, and then map directly creates a collection of the same type as the original `seq`?

---

<div class="post-metadata">

**Author:** ![SL6Trip](https://avatars.discourse-cdn.com/v4/letter/s/4da419/32.png) [@SL6Trip](https://discourse.julialang.org/u/SL6Trip)\
**Post date:** [March 27, 2021, 2:56pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/7 "2021-03-27T14:56:12Z")

</div>

I see what you mean here. I probably wouldn’t want to use that anonymous function by broadcasting either…

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [March 27, 2021, 3:40pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/8 "2021-03-27T15:40:11Z")

</div>

> [@SL6Trip](#):
>
> So broadcasting will `.` actually convert `seq` to an array, then eventually create and return something of the type that `seq` originally was

If `seq` was already an array, no conversion is required. And broadcasting always produces an array, regardless of the type of `seq`.

---

<div class="post-metadata">

**Author:** ![goretkin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goretkin/32/167_2.png) [@goretkin](https://discourse.julialang.org/u/goretkin)\
**Post date:** [March 28, 2021, 5:06pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/9 "2021-03-28T17:06:06Z")

</div>

If there are multiple arguments, then the behavior of broadcasting and `map` are quite different:

Broadcasting does… broadcasting, and `map` does not:

```julia
julia> [1] .+ [1, 2, 3]
3-element Array{Int64,1}:
 2
 3
 4

julia> map(+, [1], [1, 2, 3])
1-element Array{Int64,1}:
 2

julia> [1, 1] .+ [1, 2, 3]
ERROR: DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths 2 and 3")

julia> map(+, [1, 1], [1, 2, 3])
2-element Array{Int64,1}:
 2
 3

```

Also, broadcasting has some “symbolic” definitions for some types:

```julia
julia> (1:5) .+ (1:5)
2:2:10

julia> map(+, 1:5, 1:5)
5-element Array{Int64,1}:
  2
  4
  6
  8
 10

```

Unfortunately I don’t think there is yet a “standard” multi-argument “map” that is requires the arguments to have the same indices. (I asked [here](https://discourse.julialang.org/t/match-exact-axes-something-more-strict-than-broadcasting-or-map/41206).)

---

<div class="post-metadata">

**Author:** ![antoine-levitt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/antoine-levitt/32/4008_2.png) [@antoine-levitt](https://discourse.julialang.org/u/antoine-levitt)\
**Post date:** [March 28, 2021, 5:42pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/10 "2021-03-28T17:42:02Z")

</div>

> [@stevengj](#):
>
> `map` (or a comprehension) is probably faster if `seq` is a generic iterable collection rather than an array, since broadcasting first calls `collect` to convert iterables into arrays

Interesting, I didn’t know that. It’d be nice if generators participated in broadcast more naturally, is there an open issue about this?

---

<div class="post-metadata">

**Author:** ![SL6Trip](https://avatars.discourse-cdn.com/v4/letter/s/4da419/32.png) [@SL6Trip](https://discourse.julialang.org/u/SL6Trip)\
**Post date:** [March 29, 2021, 2:21am UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/11 "2021-03-29T02:21:52Z")

</div>

Thanks for pointing this out. I would have expected that `map` should expect matching dimensions. I think having a “strict map” can also be a good thing to avoid unexpected behavior if you accidentally pass it parameters of different sizes, especially for users that are coming from / used to Python’s `map` behavior.

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [March 29, 2021, 4:24am UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/12 "2021-03-29T04:24:05Z")

</div>

Additionally `map` is preferable for mixed-type tuples. Map will be type-stable, while broadcast wont be.

---

<div class="post-metadata">

**Author:** ![RGon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rgon/32/19515_2.png) [@RGon](https://discourse.julialang.org/u/RGon)\
**Post date:** [October 1, 2022, 3:04am UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/13 "2022-10-01T03:04:23Z")

</div>

I thought that would be the case, but look at this:

```julia
using Distributions
using BenchmarkTools
function random_dist()
       if rand() < 0.5
           return Normal(randn(), exp(randn()))
       else
           return Gamma(exp(randn()), exp(randn()))
       end
end
vdist = [random_dist() for ii in 1:1000]
tdist = Tuple(vdist)

julia> @btime map(mean, vdist);
  24.500 μs (1005 allocations: 23.62 KiB)

julia> @btime map(mean, tdist);
  755.500 μs (3492 allocations: 15.40 MiB)

julia> @btime broadcast(mean, vdist);
  38.100 μs (1498 allocations: 31.53 KiB)

julia> @btime broadcast(mean, tdist);
  738.000 μs (3493 allocations: 15.41 MiB)

julia> @btime mean.(vdist);
  37.900 μs (1500 allocations: 31.56 KiB)

julia> @btime mean.(tdist);
  2.644 μs (3 allocations: 39.25 KiB)

```

Not sure why `mean.(tdist)` is so fast, even faster than broadcast

---

<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:** [October 1, 2022, 5:46pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/14 "2022-10-01T17:46:34Z")

</div>

> [@RGon](#):
>
> Not sure why `mean.(tdist)` is so fast, even faster than broadcast

Huh. This is weird. I thought `mean.(tdist)` and `broadcast(mean, tdist)` were _exactly identical_, that the former was just syntax sugar for the latter. Apparently not:

```julia
julia> @code_lowered mean.(tdist)
CodeInfo(
1 ─ %1 = Base.broadcasted(Main.mean, x1)
│ %2 = Base.materialize(%1)
└── return %2
)

julia> @code_lowered broadcast(mean, tdist)
CodeInfo(
1 ─ nothing
│ %2 = Core.tuple(f, t)
│ %3 = Core._apply_iterate(Base.iterate, Base.Broadcast.map, %2, ts)
└── return %3
)

```

For large tuples the performance difference is dramatic. This is surprising to me.

---

<div class="post-metadata">

**Author:** ![RGon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rgon/32/19515_2.png) [@RGon](https://discourse.julialang.org/u/RGon)\
**Post date:** [October 2, 2022, 2:50pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/15 "2022-10-02T14:50:59Z")

</div>

Right? “Dot’s being syntactic sugar for broadcast” is exactly what I thought. Apparently not.

---

<div class="post-metadata">

**Author:** ![ToucheSir](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/touchesir/32/14411_2.png) [@ToucheSir](https://discourse.julialang.org/u/ToucheSir)\
**Post date:** [October 2, 2022, 4:32pm UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/16 "2022-10-02T16:32:53Z")

</div>

One reason for the `.` lowering is to enable broadcast fusion:

```julia
julia> @code_lowered exp.(log.(x))
CodeInfo(
1 ─ %1 = Base.broadcasted(Main.log, x1)
│ %2 = Base.broadcasted(Main.exp, %1)
│ %3 = Base.materialize(%2)
└── return %3
)

```

As for why dot broadcast is faster, it appears to hit [julia/broadcast.jl at v1.8.2 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/blob/v1.8.2/base/broadcast.jl#L1071) in `materialize`. This unrolls the tuple, which avoids a dynamic dispatch per element and allows for a fully type stable (i.e. not boxed) return value.

In contrast, `broadcast` hits [julia/tuple.jl at v1.8.2 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/blob/v1.8.2/base/tuple.jl#L236). That function looks a lot like a generic fallback, and it performs like one too. Based on [julia/tuple.jl at v1.8.2 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/blob/v1.8.2/base/tuple.jl#L225), you would also get unrolling if `length(tdist) < 32`.

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [October 3, 2022, 4:27am UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/17 "2022-10-03T04:27:37Z")

</div>

These tuples are much too long for meaningful benchmarks. The rule of thumb is tuples are slower than vectors above a hundred elements.

I meant if you have to apply a function to five different objects in a `Tuple`, use `map`, not broadcast:

```julia
julia> t = (1.0, 1, 1.0f0, Int16(2), Int32(0))
(1.0, 1, 1.0f0, 2, 0)

julia> @btime map(x -> 2x, t)
  17.130 ns (1 allocation: 48 bytes)
(2.0, 2, 2.0f0, 4, 0)

julia> @btime broadcast(x -> 2x, t)
  69.652 ns (2 allocations: 96 bytes)
(2.0, 2, 2.0f0, 4, 0)

```

But I think these days there are more optimisations for broadcast now if you don’t use a global variable like this, so they’re about the same.

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [October 3, 2022, 5:18am UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/18 "2022-10-03T05:18:11Z")

</div>

Note that `broadcast` is different from `.`:

```julia
julia> t = (1.0, 1, 1.0f0, Int16(2), Int32(0))
(1.0, 1, 1.0f0, 2, 0)

julia> @btime abs2.($t)
  366.188 ns (10 allocations: 352 bytes)
(1.0, 1, 1.0f0, 4, 0)

julia> @btime map(abs2, $t)
  1.458 ns (0 allocations: 0 bytes)
(1.0, 1, 1.0f0, 4, 0)

julia> @btime broadcast(abs2, $t)
  1.458 ns (0 allocations: 0 bytes)
(1.0, 1, 1.0f0, 4, 0)

julia> @btime Base.materialize(Base.broadcasted(abs2, $t))
  366.426 ns (10 allocations: 352 bytes)
(1.0, 1, 1.0f0, 4, 0)

```

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [October 3, 2022, 5:25am UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/19 "2022-10-03T05:25:16Z")

</div>

> [@Elrod](#):
>
> Note that `broadcast` is different from `.`:

but `?broadcast` says

> A special syntax exists for broadcasting: f.(args…) is equivalent to broadcast(f, args…)

---

<div class="post-metadata">

**Author:** ![N5N3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/n5n3/32/17663_2.png) [@N5N3](https://discourse.julialang.org/u/N5N3)\
**Post date:** [October 3, 2022, 5:37am UTC](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078/20 "2022-10-03T05:37:07Z")

</div>

That’s bad. Looks like `abs2.(t)` is not fully inlined thus the unrolling is not working here.

[Next page](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078.md?page=2)
