-
-
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
recursive stagedfunction and type inference #8853
Comments
👍 Would be cool to see this working. |
I can confirm that a similar thing occurs when just building a recursive staged function, but where the return value is not the recursive call and so the return type can be inferred without having to go through the recursion, as for the function In fact, running A=randn(10,10);
B=randn(10,10);
permutedimsnew!(B,A,[2,1]) just hangs julia. I didn't get it to throw a stack overflow in any reasonable time and had to force it to give up with Ctrl+C. |
Bump. I've checked and simply replacing |
I can't comment on the acceptability of your proposed fix (though it seems correct/unavoidable), but a tiny tip: when linking to line numbers, hit |
Thanks for the tip, I'll try to remember. If I don't get any response by tonight, I will just transform this fix into a pull request and see what that gives... |
I guess the only problem with it might be performance. But if |
I guess |
In my tests |
This seems to be still an issue. The fix in #9440 fixed the example above, but the following still fails: using Base.Cartesian
stagedfunction copy_strided_recursive!{N}(D,startD,stridesD::CartesianIndex{N},S,startS,stridesS::CartesianIndex{N},dims::CartesianIndex{N})
return quote
if prod(dims)<2048
# copy_strided!(D,startD,stridesD,S,startS,stridesS,dims)
else
d = $N
minstrides = min(stridesD,stridesS)
valued = (dims[d]-1)*minstrides[d]
@nif $N n->(valued<(dims[n]-1)*minstrides[n]) n->begin
d = n
valued = (dims[d]-1)*minstrides[d]
end
newdim = dims[d]>>1
newdims = @ncall $N $dims n->(n==d ? newdim : dims[n])
copy_strided_recursive!(D,startD,stridesD,S,startS,stridesS,newdims)
startD += newdim*stridesD[d]
startS += newdim*stridesS[d]
newdim = dims[d]-newdim
newdims = @ncall $N $dims n->(n==d ? newdim : dims[n])
copy_strided_recursive!(D,startD,stridesD,S,startS,stridesS,newdims)
end
return D
end
end |
Strangely enough, acting with |
Maybe somebody with some better knowledge of how type inference can briefly explain me if code_typed and actually calling a function take the same code path through type inference or not. |
This might be related to #8504 . The reason that Maybe somebody who understands inference.jl knows how this influences the rest of type inference and why recursion is recognized in one case and not in the other case. |
Current status: function test1(N)
A=Array(Float64,ntuple(N,n->2)::NTuple{N,Int});
stridesA=CartesianIndex(strides(A))
dims=CartesianIndex(size(A))
copy_strided_recursive!(A,1,stridesA,A,1,stridesA,dims)
end
function test2{N}(::Val{N})
A=Array(Float64,ntuple(N,n->2)::NTuple{N,Int});
stridesA=CartesianIndex(strides(A))
dims=CartesianIndex(size(A))
copy_strided_recursive!(A,1,stridesA,A,1,stridesA,dims)
end gives rise to the following behavior
I tried debugging inference.jl but failed to detect the problem (or get any wiser about what all of this code is doing), despite the useful HackThatBase tool of @ihnorton . However, caching the stagedfunction generation as suggested by @jakebolewski seems to fix the problem completely, at least in the following naive way as I implemented it. I just cached it in let stagedcache=Dict{Any,Any}()
function Base.func_for_method(m::Method, tt, env)
if !m.isstaged
return m.func.code
elseif haskey(stagedcache,(m,tt,env))
return stagedcache[(m,tt,env)].code
else
f=ccall(:jl_instantiate_staged,Any,(Any,Any,Any),m,tt,env)
stagedcache[(m,tt,env)]=f
return f.code
end
end
end fixes all of the above problems. Now, since this is probably not the best way or even place to cache the stagedfunction generation, I'll leave it up to somebody who knows the julia internals to implement this in a proper way, but I would like to stress that this is really pertinent. I would not consider the |
Totally agreed that the lingering bugs in stagedfunctions desperately need fixing; there's quite a lot of cleanup of base that should become possible when that happens. (E.g., #9098, needed for #9150, etc). However, your caching implementation doesn't look so bad to me---indeed, this might be the right way to do it. Care to submit it as a PR? |
One small concern: |
That's why I wanted some input, I don't know what I need to store, which part of the keys are really necessary, where else this could be used, etc. But maybe a PR with RFC is the best way to get the discussion going. |
I'm not sure about the But it's also not entirely obvious this is a problem: after all, julia has to cache the compiled function, so maybe there isn't a real problem with having a separate cache of the AST. |
I don't think it should be reverted, because redefinition is a less important feature than the fixes this provides. Agreed it's a troublesome situation, however. I suspect a lot of things would be fixable if we added a version number to methods. |
I agree, but the solution to this issue should definitely be revisited by somebody with a better knowledge/understanding of |
Yesterday, I ran into a case where the fix of #9921 was not sufficient and this problem popped up again; i.e. a recursive generated function giving rise to an infinite recursion in type inference. The expression body of my generated function contained a macro, which defined a few local variables, giving rise
On a more general note, I was wondering about the following. All of my recursive functions (generated or not) are of the form function modify_recursive!(arg1, more args ...)
if some condition
modify_base!(arg1, more args ...)
else
modify_recursive!(arg1, more args for smaller case)
end
return arg1
end I am actually wondering why type inference even bothers trying to compute the return type of the recursive call in the |
Without having any immediate answers to your questions, I'll advertise that my new favorite way to understand what's going inside |
I've used both before and they are indeed very useful. I see that you have been committing to those recently to bring them up to date with the latest master. So thanks and 👍 to all three of you! |
It seems like the stagedfunction caching fails because it compares the method, the argument types and the @generated function f{N}(a::NTuple{N,Int})
#code
end
tt, env, m = Base._methods(f,Tuple{Tuple{Int,Int,Int}},-1)[1]
tt2, env2, m2 = Base._methods(f,Tuple{Tuple{Int,Int,Int}},-1)[1]
@show env #-> svec(N,3)
@show env2 #-> svec(N,3)
@show m==m2, tt==tt2, env==env2 #-> true, true, false The problem is thus with comparing |
Well, it wasn't actually that hard, just adding the appropriate definition in |
this seems to be fixed now |
I was trying to explore the possibility of using staged functions with tuple arguments in a recursive manner, to reimplement e.g. #6517 . Therefore, I first tried to implement the Fibonacci function using staged functions, where I have an
nt::NTuple{N,Int}
argument, where the first integernt[1]
corresponds to the Fibonacci index/parameter, and I just do some trivial operations on the other values in the tuple.This is the code
and for something like
N=3
this returned expression iswhich looks ok.
However, trying to run gives rise to
and from there on it repeats this last block starting at
in abstract_call_gf at ./inference.jl:718
many times.The text was updated successfully, but these errors were encountered: