-
-
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
Vectorization Roadmap #16285
Comments
Regarding the third point, can we do As in (Hmm... that last one makes me think if |
@andyferris, without loop fusion, an in-place |
@stevengj I agree that it is (almost) useless, except for an alternative copy syntax. It's just "step zero" - it's dirt simple and provides the precedence for "step one" as doing similarly for "Step 2" would be the more complex loop fusion or similar, and perhaps generalizations to generic functions. On the whole, I do have to support what Jeff said in #14544 (comment). Perhaps the cleaner approach is to have some neat syntax for |
@andyferris, that discussion is out of date. We now do have a neat syntax for |
@stevengj Sorry, I missed the last week or so of #8450 (it's really hard to keep up with all the threads!). In any case, the progress seems very cool! The parser-based loop fusion (e.g. your #8450 (comment) seems like a great option to me. Any more complex things can still be done with loops, comprehensions, |
A |
What's the advantage? We already have that |
I'm only worry about making loop fusion everywhere. "The caller would be responsible for avoiding function calls with problematic side effects" but... How can the user use the point notation and avoid the fusion at the same time? Also I think that |
I think this is not an issue for the majority of cases.
Split the line
This is impossible. Or at least it won't be better at doing that. |
@diegozea, macros operate at the syntax level, without knowing types, so they can't check pureness. Same for the proposed Think of |
Another way of saying it is that fusing the loops is a good idea in the vast majority of cases. Not wanting to fuse is the rare exception. That makes it sensible to make fusing the default, and in the rare cases where you don't want to fuse you can either split the line or just call (Note that this whole discussion was not even possible before the |
Sorry, I meant parse not syntax level in my last sentence. However I believed that a macro could access a function metadata. |
No it can't. For a start, it doesn't even have any idea about the binding. It is allowed to do |
@diegozea, macros are called at the parse level, which is what we mean by the "syntax" level here. i.e. at the point the macro is called, all that is known is the abstract syntax tree (AST); there is no information about types or bindings. |
While fusion by default sounds like a good idea, I find it a bit unsettling that y = g.(f.(x)) and a = f.(x)
y = g.(a) might give different results. Is that just me? |
Not different results, just different ways of returning the same result. That's fine IMHO. In the long-term, the compiler might become smarter and detect more complex cases. |
@nalimilan You are assuming pure f and g. |
FWIW, I suspect most functions people are vectorizing are pure. |
Maybe we need to do some more exploration around function traits (purity, guaranteed return types, boolean, etc.) and how they could be incorporated into some of these murkier discussions (vectorization, return types, etc.). It seems like having some explicit declarations around function traits would avoid the need to rely on inference or the compiler. |
I'm worried that overly complex rules for defining how loop fusion works will just become too confusing for users. The suggestion that If it is a simple parsing-level rule, then the differences in @martinholters's comment will be obvious. If it is compile-time magic, we will be spending a lot of time trying to figure out if the compiler is really doing what we want it to do. But coming back to nested |
@andyferris Yes, the hope is that the parser level transformation can cover most use case with a well defined schematics. It is in principle possible to add support for more complicated construct (I very briefly talked about this with @andreasnoack ) but I personally feel like it is hard to come up with a syntax that can cover all the cases besides what can be currently achieved with Thinking loudly, maybe it can be achieved by having a lazy array type (so the computation is done on the fly with in |
@andyferris, the whole point is that the proposed loop fusion becomes a simple parsing-level guarantee, not compile-time at all. If you see This is very different from a compile-time optimization that may or may not occur. |
@stevengj Yes, I understand and agree completely (my first two paragraphs were directed at @quinnj).
To my taste, the keyword is "guarantee". I really do like it. I was more thinking along the lines of what @yuyichao was thinking "loudly". If Matrix-Vector multiplication was lazy (returned an iterable over (of course, when you say "compile-time" optimization I interpret that as changes to compilation after lowering, not changes to definitions in A similar thing for Matrix-Matrix multiplication is much, much harder. Although we could have arbitrarily clever iterators, they might or might not be not be the correct thing to reimplement a somewhat efficient |
@stevengj: are you planning on tackling this or should we figure out who else can tackle it? |
OK, I've gotten ChainMap into a place where it's relatively stable, and I'd like to propose an alternate method of dot vectorization based on the work there. Here's an alternative.
Here's how the syntax would change:
Splats (needs more thought, I think) Advantages over the current system:
Disadvantages:
All of this is implemented in ChainMap at the moment, aside from the new methods for map and friends. It can probably stay in a package for now if people aren't interested in adding it to base. |
I'm sorry, but I'm not seeing how this proposal would clean up code at all. What would
Would be true, but there wouldn't be less |
While we are on the topic of new ideas, yesterday @c42f and I had some fun playing with the idea of "destructuring" an array. Sometimes you have a vector of composed types ("structs"), and you might want to refer to the all the values of a certain field. Consider this operation and potential syntax we could implement here with the dot-call idea: v::Vector{Complex{Float64}}
# get the real components using current syntax
broadcast(x -> x.re, v)
broadcast(x -> getfield(x, :re), v) # alternative syntax
# Kind-of works, but need to add scalar-size methods to `Symbol`
# Also is slow because it doesn't inline the :re properly
getfield.(v, :re)
# Potential new convenience syntax (dot call on dot reference):
v..re There is also a similar thing where you might have a vector of tuples or a vector of vectors, and we can currently do something like v::Vector{Vector{Int}}
# get the first elements
getindex.(v, 1)
# potential new syntax
v.[1]
v_tuple::Vector{Tuple}
# Doesn't seem to be optimal speed (tuple indexing with Const is special)
getindex.(v_tuple, 1) To conclude: as a performance fix we could (probably should) support faster As a new syntax proposal, we might consider supporting operators |
Agreed, less verbose, more flexible.
In some cases, the amount of dots compared to ~ is cut down on. Eg. |
But |
Also, I don't see the reason for the |
Agreed that the pure math aspect of things gets lost. There are allocation free ways of going about this:
And in fact, ChainMap has some convenience macros for doing this:
But to do this you need to make assumptions about argument order (put the anonymous function first, the woven arguments last).
|
@bramtayl: if you've got a counter-proposal, you should open an issue or start a julia-dev thread. |
Ok, see #18915 |
I have a couple of questions about the third goal in this vectorization roadmap, which is to "parse operators like
The above benchmark results were obtained with the following Julia version:
|
@wshin, yes, julia> (3,4,5) .+ 7
(10,11,12)
julia> (3,4,5) .+ (8,9,10)
(11,13,15) As for your benchmark, currently there is an inlining failure that is slowing down (Of course, for very small vectors like |
Closing, since all items are now merged. Further improvements are possible, such as a generic syntax like |
Now that #15032 is merged, here are the main remaining steps discussed in #8450, roughly in order that they should be implemented:
broadcast
to be at least as good asmap
(broadcast picking incorrect result type #4883). The trickest part is the ongoing discussion of what to do in the empty-array case (see Coherent map and comprehension, the imminent death of type_goto #11034).@vectorized
functions likesin(x)
in favor ofsin.(x)
. Deprecate functions vectorized via@vectorize_(1|2)arg
in favor of compact broadcast syntax #17302a .+ b
asbroadcast(+, a, b)
. (See also support many more Unicode infix operators #6929 (comment), Allow users to define "dot" vectorized operators. #14544, and parse more dot operators, parse .= with assignment precedence #17393.) Existing overloaded functions.+(a, b) = ...
can be deprecated in favor of overloadingbroadcast(::typeof(+), a, b)
. make "dot" operations (.+ etc) fusing broadcasts #17623More speculative proposals
probably for the 0.6 timeframe(suggested by @yuyichao):f.(args...)
calls as "fusing" broadcast operations at the syntax level. For example,sin.(x .+ cos.(x .^ sum(x.^2)))
would turn (injulia-syntax.scm
) intobroadcast((x, _s_) -> sin(x + cos(x^_s_)), x, sum(broacast(^, x, 2)))
. Notice that thesum
function (or any non-dot call) would be a "fusion boundary." The caller would be responsible for not usingf.(args...)
in cases where fusion would screw up side effects. fusion of nested f.(args) calls into a single broadcast call #17300x .= ...
as "fusing" calls tobroadcast!
, e.g.x .= sin.(x .+ y)
would act in-place with a single loop. Again, this would occur at the syntax level, so the caller would be responsible for avoiding function calls with problematic side effects. (See make .+= et al. mutating #7052, but the lack of loop fusion at that time made.+=
much less attractive.) treat .= as syntactic sugar for broadcast! #17510The text was updated successfully, but these errors were encountered: