Why does 'map' recompile each time?

Consider the follosing example:

y = [1 2 3]
@time findall(map( x-> x.== 3, y))
 0.077566 seconds (69.12 k allocations: 4.160 MiB, 13.08% gc time, 99.57% compilation time)
1-element Vector{CartesianIndex{2}}:
 CartesianIndex(1, 3)

Why is the function recompiled each time I call it?

Because you’re defining a new anonymous function for each invocation.

3 Likes

To see this:

julia> f = x -> x .== 3
#57 (generic function with 1 method)

julia> @time findall(map(f, y))
  0.030139 seconds (72.98 k allocations: 4.008 MiB, 98.57% compilation time)
1-element Vector{CartesianIndex{2}}:
 CartesianIndex(1, 3)

julia> @time findall(map(f, y))
  0.000016 seconds (3 allocations: 160 bytes)
1-element Vector{CartesianIndex{2}}:
 CartesianIndex(1, 3)
2 Likes

The dot in .== is for broadcasting, it ‘maps’ the operator over its input collection. So it’s redundant here, the argument of map is also mapped over the collection. Therefore, you should drop it: map( x->x==3, y), not map( x-> x.== 3, y).

Furthermore, findall takes a ‘mapped’ function as its first argument, too, so you can just do this instead:

findall( x->x==3, y)

Or, more tersely,

findall(==(3), y)

No reason to use findall, map and broadcast, when just findall will do.

2 Likes

Note that if it’s inside a function, a closure will only be compiled once:

julia> y = [1, 2, 3];

julia> @time findall(x -> x == 3, y);
  0.038798 seconds (79.31 k allocations: 4.512 MiB, 99.87% compilation time)

julia> foo(y) = findall(x -> x == 3, y);

julia> @time foo(y);
  0.037377 seconds (80.87 k allocations: 4.604 MiB, 99.96% compilation time)

julia> @time foo(y);
  0.000003 seconds (1 allocation: 80 bytes)
2 Likes