Skip to content
This repository was archived by the owner on May 4, 2019. It is now read-only.

Commit 1190154

Browse files
committed
Use generated function to avoid allocation
This only covers one- and two-argument cases.
1 parent 4d1a1f8 commit 1190154

File tree

1 file changed

+56
-39
lines changed

1 file changed

+56
-39
lines changed

src/broadcast.jl

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -31,28 +31,33 @@ else
3131
using Base.Broadcast: ziptype
3232
end
3333

34-
@inline function broadcast_lift(f, x)
35-
if null_safe_op(f, eltype(x))
36-
return @compat Nullable(f(x.value), !isnull(x))
34+
@inline @generated function broadcast_lift{F, T}(f::F, x::NTuple{1, T})
35+
if null_safe_op(f.instance, eltype(T))
36+
return :( Nullable(f(unsafe_get(x[1])), !isnull(x[1])) )
3737
else
38-
U = Core.Inference.return_type(f, Tuple{eltype(x)})
39-
if isnull(x)
40-
return Nullable{U}()
41-
else
42-
return Nullable(f(unsafe_get(x)))
38+
U = Core.Inference.return_type(f.instance, Tuple{eltype(T)})
39+
return quote
40+
if isnull(x)
41+
return Nullable{$U}()
42+
else
43+
return Nullable(f(unsafe_get(x[1])))
44+
end
4345
end
4446
end
4547
end
4648

47-
@inline function broadcast_lift(f, x1, x2)
48-
if null_safe_op(f, eltype(x1), eltype(x2))
49-
return @compat Nullable(f(x1.value, x2.value), !(isnull(x1) | isnull(x2)))
49+
@inline @generated function broadcast_lift{F, S, T}(f::F, x::Tuple{S, T})
50+
if null_safe_op(f.instance, eltype(S), eltype(T))
51+
return :( Nullable(f(unsafe_get(x[1]), unsafe_get(x[2])),
52+
!(isnull(x[1]) | isnull(x[2]))) )
5053
else
51-
U = Core.Inference.return_type(f, Tuple{eltype(x1), eltype(x2)})
52-
if isnull(x1) | isnull(x2)
53-
return Nullable{U}()
54-
else
55-
return Nullable(f(unsafe_get(x1), unsafe_get(x2)))
54+
U = Core.Inference.return_type(f.instance, Tuple{eltype(S), eltype(T)})
55+
return quote
56+
if isnull(x[1]) | isnull(x[2])
57+
return Nullable{$U}()
58+
else
59+
return Nullable(f(unsafe_get(x[1]), unsafe_get(x[2])))
60+
end
5661
end
5762
end
5863
end
@@ -62,29 +67,34 @@ eltypes(x) = Tuple{eltype(x)}
6267
eltypes(x, xs...) = Tuple{eltype(x), eltypes(xs...).parameters...}
6368

6469
"""
65-
broadcast_lift(f, xs...)
70+
broadcast_lift(f, x)
6671
67-
Lift function `f`, passing it arguments `xs...`, using standard lifting semantics:
68-
for a function call `f(xs...)`, return null if any `x` in `xs` is null; otherwise,
69-
return `f` applied to values of `xs`.
72+
Lift function `f`, passing it arguments from the tuple `x`, using standard lifting semantics:
73+
for a function call `f(x...)`, return null if any `x` in `x` is null; otherwise,
74+
return `f` applied to values of `x`.
7075
"""
71-
@inline function broadcast_lift(f, xs...)
72-
if null_safe_op(f, eltypes(xs).parameters...)
76+
@inline @generated function broadcast_lift{F}(f::F, x::Tuple)
77+
if null_safe_op(f.instance, eltypes(x).parameters...)
7378
# TODO: find a more efficient approach than mapreduce
7479
# (i.e. one which gets lowered to just isnull(x1) | isnull(x2) | ...)
75-
return @compat Nullable(f(unsafe_get.(xs)...), !mapreduce(isnull, |, xs))
80+
return :( Nullable(f(unsafe_get.(x)), !mapreduce(isnull, |, x)) )
7681
else
77-
U = Core.Inference.return_type(f, eltypes(xs...))
78-
# TODO: find a more efficient approach than mapreduce
79-
# (i.e. one which gets lowered to just isnull(x1) | isnull(x2) | ...)
80-
if mapreduce(isnull, |, xs)
81-
return Nullable{U}()
82-
else
83-
return Nullable(f(map(unsafe_get, xs)...))
82+
U = Core.Inference.return_type(f.instance, eltypes(x))
83+
return quote
84+
# TODO: find a more efficient approach than mapreduce
85+
# (i.e. one which gets lowered to just isnull(x1) | isnull(x2) | ...)
86+
if mapreduce(isnull, |, x)
87+
return Nullable{$U}()
88+
else
89+
return Nullable(f(map(unsafe_get, x)...))
90+
end
8491
end
8592
end
8693
end
8794

95+
call_broadcast{F, N}(f::F, dest, As::Vararg{NullableArray, N}) =
96+
invoke(broadcast!, Tuple{Function, AbstractArray, Vararg{AbstractArray, N}}, f, dest, As...)
97+
8898
"""
8999
broadcast(f, As::NullableArray...)
90100
@@ -97,15 +107,19 @@ Note that this method's signature specifies the source `As` arrays as all
97107
of both `Array`s and `NullableArray`s will fall back to the standard implementation
98108
of `broadcast` (i.e. without lifting).
99109
"""
100-
function Base.broadcast{N}(f, As::Vararg{NullableArray, N})
101-
f2(x...) = broadcast_lift(f, x...)
110+
function Base.broadcast{F, N}(f::F, As::Vararg{NullableArray, N})
111+
# These definitions are needed to avoid allocation due to splatting
112+
f2(x1) = broadcast_lift(f, (x1,))
113+
f2(x1, x2) = broadcast_lift(f, (x1, x2))
114+
f2(x...) = broadcast_lift(f, x)
115+
102116
T = _default_eltype(Base.Generator{ziptype(As...), ftype(f2, As...)})
103117
if isleaftype(T) && !(T <: Nullable)
104118
dest = similar(Array{eltype(T)}, broadcast_indices(As...))
105119
else
106120
dest = similar(NullableArray{eltype(T)}, broadcast_indices(As...))
107121
end
108-
invoke(broadcast!, Tuple{Function, AbstractArray, Vararg{AbstractArray, N}}, f2, dest, As...)
122+
call_broadcast(f2, dest, As...)
109123
end
110124

111125
"""
@@ -120,15 +134,18 @@ source `As` arrays as all `NullableArray`s. Thus, calling `broadcast!` on a argu
120134
consisting of both `Array`s and `NullableArray`s will fall back to the standard implementation
121135
of `broadcast!` (i.e. without lifting).
122136
"""
123-
function Base.broadcast!{N}(f, dest::NullableArray, As::Vararg{NullableArray, N})
124-
f2(x...) = broadcast_lift(f, x...)
125-
invoke(broadcast!, Tuple{Function, AbstractArray, Vararg{AbstractArray, N}}, f2, dest, As...)
137+
function Base.broadcast!{F, N}(f::F, dest::NullableArray, As::Vararg{NullableArray, N})
138+
# These definitions are needed to avoid allocation due to splatting
139+
f2(x1) = broadcast_lift(f, (x1,))
140+
f2(x1, x2) = broadcast_lift(f, (x1, x2))
141+
f2(x...) = broadcast_lift(f, x)
142+
call_broadcast(f2, dest, As...)
126143
end
127144

128145
# To fix ambiguity
129-
function Base.broadcast!(f, dest::NullableArray)
130-
f2(x...) = broadcast_lift(f, x...)
131-
invoke(broadcast!, Tuple{Function, AbstractArray, Vararg{AbstractArray, 0}}, f2, dest)
146+
function Base.broadcast!{F}(f::F, dest::NullableArray)
147+
f2() = broadcast_lift(f, ())
148+
call_broadcast(f2, dest)
132149
end
133150

134151
# broadcasted ops

0 commit comments

Comments
 (0)