-
-
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
Make quadgk type stable #18928
Make quadgk type stable #18928
Conversation
It would be nice, if one could find a way to ensure type stability in all cases. But I don't know how to achieve this. |
Cc @stevengj |
b::Number | ||
I | ||
E::Real | ||
immutable Segment{T1, T2, T3} |
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.
T1<:Number
and T3<:Real
?
Looking back at this code, it seems like quadgk(f, a, b, c...; kws...) = quadgk(f, promote(float(a),float(b),c...)...; kws...) In addition to shortening the code, wouldn't this help type-stability in the case where the limits are different types, since |
Sorry, it seems, that I was misled by
On the other hand:
Is there any real benefit of a correctly inferred return type by |
The simplified code for the conversion seems to work, but it does not improve the output of After further investigation, it seems that the parameters of Unfortunately, without parameters, I can't avoid the |
Could we perhaps get some tests for the type stability? |
I don't see how the additional function call could slow things down that much, and using parameterized types normally makes things faster, not slower. Is the |
heappush!(segs, evalrule(f, s[i],s[i+1], x,w,gw, nrm), Reverse) | ||
segs = [evalrule(f, s[1],s[2], x,w,gw, nrm)] | ||
for i in 2:length(s) - 1 | ||
heappush!(segs, evalrule(f, s[i], s[i+1], x,w,gw, nrm), Reverse) |
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.
I'm worried that this will fail if f
is not type-stable. e.g. if it returns an Int
sometimes and a Float64
other times. We want that case to work (even if it is slower).
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.
I guess this is not necessary any more, anyway, as the parameter for Segment
slowed everything down significantly if the evaluation of f
is fast. The only useful change would be to introduce the do_quadgk_no_inf
function to avoid the recursion if one wants a fast version for Inf
at the cost of the non-Inf
case.
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.
Sorry, I take that back. The real reason for the slowdown in the Inf
case is the reuse of s
, s1
, and s2
. Just changing these names and annotating s0::eltype(s)
gives a 3x speedup and no slowdown for the finite case. I think this is what should be done.
As I said above, the only change that introduced a speedup was the rename of the segment variables. There seems to be no way to get fast code and an inferred type in |
@ti-s, a PR with only the rename (and maybe my suggestion for the shortened promotion code) seems like a good idea. The |
Due to the reuse of some variables, there was a slowdown in the case of infinite limits.
9a8ae01
to
d6a303a
Compare
Sorry, there is still something going on. This looks nice:
But this not:
The runtime does not go below 6s even after several calls to Edit: Likewise, in the first case the time of the third comprehension of |
Introducing a new type parameter instead of the call to Moving the second part of the body of Now the timings look like this:
There is a 20% runtime regression for the case of finite limits, but I think it is worth the more than 3x performance increase for It might be still interesting to find the reason for the strange behaviour that the runtime depends on the order of compilation, but this is outside the scope of this PR. Edit: I also removed the type annotation of |
This version can throw an error for type unstable functions.
I was finally able to get a speedup in all cases after reading #19137.
Additionally
The compilation now takes a bit longer, but is still about 3x faster than for the version in master. The downside is, that I don't know how to make this line work with type unstable functions
But without that, I can't improve on the timings of the previous version. Any idea? |
Would |
Thanks! Didn't know about
In that case the first problem is
And if I remove the
This seems to work, but the timings are suboptimal again:
At least with the simple example |
The problem with your last solution is that Maybe define: segment_type{Tx,Tf<:Number}(::Type{Tx}, ::Type{Tf}) = Segment{Tw, Tf, real(Tf)}
segment_type{Tx,Tf<:Number}(::Type{Tx}, ::Type{Array{Tf}}) = Segment{Tw, Tf, real(Tf)}
segment_type(::Type, ::Type) = Segment and then do |
Or maybe have |
The solution with |
As I mentioned above, defining
works well for my test cases. It is fast and also gives (note the mixed types of the limits)
But unfortunately that fails, if a user supplied Additionally, this runtime regression could be a result of the specialization on
With the version on master:
With commit 108c8fd
Also, on 0.5, the runtime of this PR depends again on the compilation order, but that seems to be fixed on master. To summarize:
Finally, I should probably write some tests if you decide to merge this PR. |
The reason for the slowdown in my previous comment is that
If I fix that, the new version is not slower than the version on master:
New version:
Version on master:
|
Could you reopen this at https://github.com/JuliaMath/QuadGK.jl ? |
The version in master always returns Tuple{Any, Any} which might effect the calling code. This pull request changes this behavior if all limits are of the same subtype of AbstractFloat. Otherwise the return type can still not be inferred.