-
-
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
in-place assignment operator? #249
Comments
FFT various linalg functions from LAPACK Also array arithmetic operations such as += etc. |
|
We might want to change the way update operators like |
That definitely seems like a good thing to me. We want to be able to do things like |
Actually this is a bit tricky. If we have a fallback definition of |
For example, consider |
I'm worried that this makes code less generic. If
calls
? |
Note the three argument version is needed for calling GEMM. |
I seems like a bit of a train wreck. If |
Basically I don't want to have to think about whether to write This is solved by using any syntax other than |
So this would mean that we can write things like |
This is a really excellent reason to have this. |
How about having ternary forms like |
Changing the title from the original "in-place versions of functions" since there are a lot of those now and people keep adding more. Now we just need to decide if we want some kind of in-place assignment operator or not. |
I should point out that in places where I have needed in place assignment, I have been able to use stuff like |
Hi guys, this is an interesting discussion, and a thorny problem. Here's a naive suggestion for the in-place operators like
with some sensible default implementation of
Of course, this depends on the compiler being able to optimize the branch away for good performance. I don't have enough experience with julia's dynamic type system to know whether this is plausible in the cases you care about performance. |
#3424 is related to this |
|
This issue is not that it's impossible to achieve different behavior for mutable vs. immutable – the issue is that it's not desirable because that sabotages generically written code. For example, if you write |
Hmm. On further thought what I proposed is rather similar to Jeff's I remain hopeful that there's a solution which could make |
+1 for the element-wise assignment operator |
+1 for element-wise assignment via |
@c42f, regarding NumPy, the thing is that one doesn't tend to write a lot of highly generic code in NumPy, and certainly not code that might be used on both mutable and immutable types of values. Then again, it's not 100% clear to me that this is actually a sufficiently realistic situation for Julia, but it still makes me uncomfortable enough that I don't want to do this without considering all the alternatives. |
While I appreciate the technical difference between
in which we have the same result, but with much uglier code. |
I suspect how often people would ever write codes like if immutable(x)
x += 1
else
x .+= 1
end If you really want to write something as above and want the code beautiful, you can still write if isa(x, AbstractArray)
for i in 1 : length(x)
x[i] += 1
end
else
x += 1
end I don't think this is any prettier than the code above. The introduction of I have plenty of examples in writing numeric computation, machine learning, and image processing algorithms that need inplace updating of arrays --- to the extent that I feel I have to create a package and introduce functions like I have a strong opinion about this, as I do such computation in a daily basis. |
@NHDaly, you're interpreting |
I see... To be honest that is terribly unclear. I think the language would I'm excited to see how this turns out! :)
|
This immutable/mutable stuff is just way too subtle a distinction for most people. It is not intuitive that |
y = a
[...]
x = a
[...]
x += 1
[...]
print(y) y = a
[...]
x = a
[...]
x = x+1
[...]
print(y) I don't think that having those two pieces of code have different answers is at all intuitive |
This doesn't warrant any further discussion at this point and the mutable vs immutable distinction is completely orthogonal – the mutability of a type has no effect on the meaning of operations on it. We have a large number of in-place operators at this point, which is what this issue was originally opened to address. |
Is this a definitive decision against including an in-place assignment operator in the language? It seems like the original question got lost in the mutable vs immutable discussion. |
I don't think anyone has a credible proposal for what such an operator would do. Everyone agrees the situation could be improved, but no seems to know how to do it. |
Then shouldn't the issue be left open until someone figures it out? If the issue is closed, there is a chance no one will revisit it in the future. |
People can reasonably disagree about that point, but I personally think that's not an accurate assessment of how work gets done by most of the Julia developers. I prefer the open issues to be clearly actionable. But you don't need an issue to be free to work on something. |
I guess that's reasonable. I definitely don't want this issue to be forgotten because it would make a big difference in much of the code I write. |
This particular issue has gotten too long and muddle at this point. It was also originally about adding more in-place operations, which we now have lots of. The issue of in-place operation will not be forgotten, I can assure you. |
Fascinating. Thanks for the update! |
It might be useful for f!(args...) to be parsed to inplace(x, f, args...) and we could take advantage of new closure types to do: inplace(x, ::typeof(f), args...) = f_in_place(x, args...) where |
What's the gain? Just can't know for a general |
Yes, I do think you need to define both anyway. But having ! exposed to the parser would allow it to hook into any forthcoming more general loop fusion syntax (see #16285) |
fix tests to work with #16590
Now is 2018, having reached some conclusions yet? Somehow the gotcha 6 in this post still exists.
will still produce 48! Maybe 7 years is too short to make such a "huge" improvement? |
Maybe reading the very first section of the performance tips is too much to ask? |
@KristofferC (I am trying to improve the performance of SparseMatrixCSC * Diagonal) using SparseArrays
using LinearAlgebra
import LinearAlgebra:rmul!, mul!, lmul!
import Base: *
function rmul!(X::SparseMatrixCSC, A::Diagonal)
nA = size(A, 1)
mX, nX = size(X)
nX == nA || throw(DimensionMismatch())
@inbounds for j = 1:nA
X.nzval[X.colptr[j]:X.colptr[j+1]-1] .*= A.diag[j]
end
X
end
using BenchmarkTools
const N = 1000
const dg = Diagonal(randn(ComplexF64, 1000))
const sp = SparseMatrixCSC(dg)
@benchmark rmul!(sp, dg) It has 1000 allocations, although using Could you please explain it? (I promise I have already read the performance tips many times, lol) |
It seems the view created from the broadcasted assignment is not elided and if the length of A MWE is julia> f(a) = (a[1:100] .*= 2; a)
f (generic function with 1 method)
julia> a = ones(1000);
julia> @btime f($a);
112.160 ns (1 allocation: 896 bytes) |
That seems worth opening a separate specific issue about, no? |
This is an example of a place where call-site inlining is essential. It felt a little wrong to always inline |
Examples:
sort!
conj!
transpose!
— interesting question: is the operator version.'!
?ctranspose!
— likewise: is the operator version'!
?Please add others as you think of them. Obviously, they need to be operations that cannot change the type of the array. @ViralBShah: are there in-place FFTW operations that we should expose?
The text was updated successfully, but these errors were encountered: