-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add function + two-argument method to reducers #35017
Conversation
@@ -651,6 +651,8 @@ for (fname, _fname, op) in [(:sum, :_sum, :add_sum), (:prod, :_prod, | |||
# User-facing methods with keyword arguments | |||
@inline ($fname)(a::AbstractArray; dims=:) = ($_fname)(a, dims) | |||
@inline ($fname)(f, a::AbstractArray; dims=:) = ($_fname)(f, a, dims) | |||
@inline ($fname)(f, a::AbstractArray, b::AbstractArray; dims=:) = | |||
($_fname)(((a,b),)->f(a,b), zip(a,b), dims) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If so, why don't make it more general? sum(f, args...)
as _sum(args->f(args...), zip(args...))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If so, why don't make it more general?
sum(f, args...)
as_sum(args->f(args...), zip(args...))
This could even replace the single argument method, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could, but I will benchmark that first to ensure there's not a performance hit for the zip.
Here's an imperfect regexp to see how common this is in practice |
Just as an alternative, you can also do julia> a,b = rand(10), rand(10);
julia> @btime sum(-, $a, $b)
19.606 ns (2 allocations: 48 bytes)
-1.6367587009468854
julia> @btime sum(Base.splat(-), zip($a, $b))
19.506 ns (2 allocations: 48 bytes)
-1.6367587009468854 |
FYI there is #31020 which is more composable IMHO. |
Thanks for your comments.
|
We've talked about reducers taking multiple arguments (and what it means) in the context of |
The discussion on the syntax is in #19198. I think it's reasonable to take time to decide the syntax. Meanwhile, we can write |
Closing this in favor of lazy broadcasting |
Motivation
The following style of code is very common in practice
While it is easy to apply a function to all arguments without creating a temporary array and causing allocations, by means of the method
sum(f,x)
, it is slightly more cumbersome to do so when the function takes two arguments. This PR adds such a method to all reduction functions (such assum,maximum
etc.) accepting two arrays, implemented simply by means of zipping the two arrays and calling thereducer(f,x)
method.Common combinations like
sum((a.-b).^2)
can easily be realized assum(abs2 ∘ -, a, b)
.mapreduce(-, +, a, b)
does infact solve exactly this problem, but it is horribly slow compared to both alternatives above.Some benchmarks
In the table below, "Eager" denotes
sum(a .- b)
for various lengths of vectors. "Functional" denotessum(-, a, b)
. The middle colums are the timings and the "Reduction" column are the relative timingsfunctional/eager
meaning that values below 1 are faster for the method in this PR.Naturally, the eager method allocates linear memory whereas the functional allocates constant
15.596 ms (2 allocations: 48 bytes)
As for the timings, the functional approach is about 2x faster for very small arrays and very large arrays, whereas results are more even for medium sized arrays.
Benchmark code