Trace of broadcasting

docs said:

f.(args...) is equivalent to broadcast(f, args...)

however, I found that f.() is not calling broadcast(f, ):

Base.broadcast(::typeof(exp), x) = println("exp ", x);
x = [1.0, 2.0];

julia> Base.broadcast(exp, x)
exp [1.0, 2.0]

julia> exp.(x)
2-element Array{Float64,1}:
 2.718281828459045 
 7.3890560989306495

in above, exp.() is not calling the broadcast() I tried to override. Is there any method to enforce it? thanks. :pray:

If you want to customize broadcasting, the relevant documentation is

https://docs.julialang.org/en/v1.3/manual/interfaces/#man-interfaces-broadcasting-1

I mean, why f.() not compiled into broadcast(f, ) ?

I don’t know the answer to that, but in case you didn’t know, here’s how to check what is being called:

julia> @code_lowered (()->f.(x))()
CodeInfo(
1 ─ %1 = Base.broadcasted(Main.f, Main.x)
β”‚   %2 = Base.materialize(%1)
└──      return %2

Alternatively:

julia> Meta.@lower f.(x)
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─ %1 = Base.broadcasted(f, x)
β”‚   %2 = Base.materialize(%1)
└──      return %2
))))

broadcast(f, x) needs to return an array if x is an array. If f.(g.(x)) is lowered to broadcast(f, broadcast(g, x)), it would need to create intermediate array. Instead, julia does

julia> Meta.@lower f.(g.(x))
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─ %1 = Base.broadcasted(g, x)
β”‚   %2 = Base.broadcasted(f, %1)
β”‚   %3 = Base.materialize(%2)
└──      return %3
))))

If you read the manual you’d notice that broadcasted (note ed) does not do the actual computation. Rather, the actual computation is done in materialize without creating any intermediate arrays.

It’s actually broadcast(f, ) that calls (the lowered version of) f.():

3 Likes