-
-
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
Documented abs overflow behavior, added checked_abs for overflow prot… #13841
Conversation
The following might be a bit faster: (I have to run, Halloween for the kids) function checked_abs{T<:Signed}(v::T)
(v = abs(v)) < 0 && throw(InexactError())
v
end |
Btw, very nice PR!:+1: |
@ScottPJones, interesting suggestion for the implementation of As for
but
Still, I like |
For this to appear in the manual I believe you have to add .. function:: abs(x::Signed) and .. function:: checked_abs(x::Signed) to the correct places in the |
@KristofferC thanks for the pointer on properly linking in-code documentation to the manual. I'll investigate that soon on my fork. Right now, my Julia working day is close to its end... |
This is on Julia nightly
|
You are almost certainly benchmarking mainly the overhead. julia> using Benchmarks
julia> function checked_abs{T<:Signed}(x::T)
x == typemin(T) && throw(OverflowError())
abs(x)
end
checked_abs (generic function with 1 method)
julia> function checked_abs2{T<:Signed}(v::T)
(v = abs(v)) < 0 && throw(InexactError())
v
end
checked_abs2 (generic function with 1 method)
julia> @benchmark checked_abs(1)
================ Benchmark Results ========================
Time per evaluation: 2.13 ns [2.11 ns, 2.15 ns]
Proportion of time in GC: 0.00% [0.00%, 0.00%]
Memory allocated: 0.00 bytes
Number of allocations: 0 allocations
Number of samples: 11601
Number of evaluations: 126646601
R² of OLS model: 0.961
Time spent benchmarking: 0.51 s
julia> @benchmark checked_abs2(1)
================ Benchmark Results ========================
Time per evaluation: 3.40 ns [3.37 ns, 3.44 ns]
Proportion of time in GC: 0.00% [0.00%, 0.00%]
Memory allocated: 0.00 bytes
Number of allocations: 0 allocations
Number of samples: 11401
Number of evaluations: 104667101
R² of OLS model: 0.971
Time spent benchmarking: 0.51 s
julia> @benchmark abs2(1)
================ Benchmark Results ========================
Time per evaluation: 1.80 ns [1.78 ns, 1.82 ns]
Proportion of time in GC: 0.00% [0.00%, 0.00%]
Memory allocated: 0.00 bytes
Number of allocations: 0 allocations
Number of samples: 11401
Number of evaluations: 104667101
R² of OLS model: 0.958
Time spent benchmarking: 0.50 s
julia> @benchmark checked_abs(-1)
================ Benchmark Results ========================
Time per evaluation: 2.03 ns [2.01 ns, 2.05 ns]
Proportion of time in GC: 0.00% [0.00%, 0.00%]
Memory allocated: 0.00 bytes
Number of allocations: 0 allocations
Number of samples: 11901
Number of evaluations: 168565701
R² of OLS model: 0.964
Time spent benchmarking: 0.51 s
julia> @benchmark checked_abs2(-1)
================ Benchmark Results ========================
Time per evaluation: 3.38 ns [3.34 ns, 3.42 ns]
Proportion of time in GC: 0.00% [0.00%, 0.00%]
Memory allocated: 0.00 bytes
Number of allocations: 0 allocations
Number of samples: 11801
Number of evaluations: 153241801
R² of OLS model: 0.954
Time spent benchmarking: 0.67 s
julia> @benchmark abs(-1)
================ Benchmark Results ========================
Time per evaluation: 1.90 ns [1.88 ns, 1.92 ns]
Proportion of time in GC: 0.00% [0.00%, 0.00%]
Memory allocated: 0.00 bytes
Number of allocations: 0 allocations
Number of samples: 10201
Number of evaluations: 33352001
R² of OLS model: 0.979
Time spent benchmarking: 0.50 s |
@Mike43110 I'm afraid that you copied my first and admittedly bad attempt at benchmarking |
@MichaeLeroy I'll try benchmarking it myself tomorrow, I had just had time to look at the output of |
@ScottPJones, you might want to have a look at @yuyichao's contribution to the discussion. He's already done some good looking benchmarks of the two versions of |
@MichaeLeroy 1) I haven't found the macro yet that @yuyichao is using (I did Pkg.add("Benchmark"), which doesn't seem to be updated for 0.4.0 yet), but it only has functions, not a macro. 2) His results go against what I know from benchmarking Intel chips for >35 years 3) Now that I've had time to do my own benchmarking, I get very different results - in fact, the numbers from the 3 methods are so close, that I think abs() should simply be replaced by checked_abs(). 4) The code generated seems very different between v0.4.0 and v0.5 (freshly built from master), and depending on whether it is LLVM 3.3 (the default), LLVM 3.7 or LLVM-svn. Here are some results, on 0.5 (built today), LLVM 3.3, 64-bit Mac OS X: As you can see, the abs() function takes about the same time as doing nothing at all, |
Here is a gist with my benchmarking code: I'd love to see the results on your machine! |
@ScottPJones sorry but as I said above, I'm not terribly interested in getting deep into the weeds with benchmarking to look for marginal differences between So far, I've seen nothing in the arguments by benchmark that convince me to revisit the version of OK, I've disarmed myself by indicating a lack of competence and immediate interest in deep-dive benchmarking. I've also declined to take a look at the proffered gist. Still I can't resist a couple of comments on the latest benchmark numbers that you present. The large allocations and significant gc activity strike me as inconsistent with testing focused on the performance of the flavors of |
@ScottPJones Since |
@simonster Thanks! I'd looked at the native code of the 3 different abs versions, as well as with the test functions, and didn't understand why there was so much stuff there. Could the compiler figure that out, and not do all of that boxing and memory allocation? @MichaeLeroy A full explanation would take a while, and the version in the PR is just fine, due to the way modern processors operate (I'm not sure what would happen on an ARM chip though!) (which is also why adding the check doesn't really slow things down, because the CPU has multiple ALUs, and can also handle speculative execution). I was just surprised that @yuyichao's benchmarks showed such a difference - and wanted to understand just what was causing that effect in Julia. I do think this is a very fine PR, and hopefully will be included soon. |
FWIW, I think the implementation in this PR looks good and I'm on board with the general idea, but it should have tests. |
@simonster thanks for your contributions to the discussion. I should be clear that my expressed scepticism of benchmarking is limited to the sort of argument by benchmarking (alone) over alternative implementations of I'm all for having good benchmarks of |
Agreed that further benchmarking is unnecessary at this point. You just need to add a couple lines somewhere in @test checked_abs(-1) == 1
@test_throws OverflowError checked_abs(typemin(Int)) These tests are run automatically before we merge pull requests or make releases, or you can run them manually by running Usually you want to make sure the tests cover all lines and all branches that could be hit. There's documentation on Thank you for your contribution! |
The RST doc still needs to be updated. However, given that this is basically ready to merge and there's some updates in the inline doc that are not sync to the RST doc yet, I'd prefer to just merge this without the sync (so please just add the test @simonster suggested) and I'll include the doc update in #13837 once the pending issue in that PR is sorted out. |
@simonster and @yuyichao thanks for your inputs. I have only a bit of work time left in my day (and am frazzled from the |
@MichaeLeroy take your time. |
@yuyichao and @simonster adding and running the suggested tests was not hard. (I did loop through the Signed types for thoroughness.) That and I managed the mechanics of github. So I think this PR is good to go. Thanks again for your help. Oops AppVeyor failed.... |
...trailing white space in test file. I've tried a fix. |
OK, looks good now. |
@MichaeLeroy, I just wanted to say that this is an exemplary first pull request. Please disregard @ScottPJones's comments and benchmarks since they are noise. |
@StefanKarpinski thanks for your kind words. I've found Julia to be a remarkable language with great promise, certainly the coolest I've had the pleasure to use. I'll try to contribute what I can as time and talent allow. It's fun. |
Documented abs overflow behavior, added checked_abs for overflow prot…
Yep, @MichaeLeroy you're doing everything right. Welcome, thanks for the contribution, and I hope we'll have the chance to see more from you in the future! |
I'm coming a little late to the party, but wouldn't it make sense to define |
@nalimilan I certainly considered, and vacillated if not agonized, over that question. And I did mention it in opening the PR. While generally speaking, being as generic as reasonably possible is desirable, I envision |
@nalimilan BTW, if you are importing
So maybe it's not that much of a restriction that, as implemented, |
@MichaeLeroy I agree it's easy to define manually when you need it, but my point was rather about doing the right thing from the start, which is what makes Julia great to use: people don't need to spend time on each method for each type to get things to work. For example, |
Are we really sure that checked_abs(x) = abs(x) is safe as a fallback for all non-Signed types? We really don't want to provide a fallback that could silently provide the wrong behavior. |
@nalimilan I guess that we have a bit of a philosophical difference on this issue. (Not, on generics in general but on this function in particular.) That's not a big deal from my perspective, certainly not a line in the sand. In fairness, I did attempt to solicit a discussion on this issue by explicitly calling attention to it in the PR and acknowledging the validity of the fully generic approach. I had hoped to see some discussion of it to get the community's consensus. So I'm certainly interested in seeing this issue aired. But after all of the work that I put into responding to the weedy discussions of alternative implementations and of benchmarking, I don't feel that I have the time and energy just now to take a leading role in this further discussion. I don't know what the appropriate protocol is for re-opening/continuing a discussion over a closed PR, but feel free to do what's right. BTW, #13549, which lead to this PR remains open pending the creation of a FAQ entry on Integer overflow, and I'm happy to get comments, criticism and advice on this work. If you do open a new issue or PR on this question, please do ping me, so I'll have an opportunity to follow the discussion. |
@toivoh I think that you are right that it would be improper merely to fall back to |
@MichaeLeroy No worries, your PR is arguably an improvement, and the changes I propose can be made at any point if they are deemed useful. At least the current situation is completely safe. You're both right that the fallback wouldn't be 100% safe for all non- If other people support this idea, I can make another PR (unless Michael wants to do it). |
@nalimilan I'd be happy to have you create another PR to expand the domain of |
I've made a PR: #13912 |
…ection
Summary
This PR is to address, in part, #13549. It adds documentation to the signed integer version of
abs
that explains its overflow behavior. It also adds an overflow trapped version of this function,Base.checked_abs
. As yet unaddressed is the addition of an Integer overflow entry to Julia's FAQ.abs
DocumentationWith this change, the documentation for
abs
reads:It is interesting that the added signed integer specific
abs
documentation is printed prior to the already existing generic documentation. As far as I can tell, such presentation is a result of the design of the documentation system, which presents documentation in order of descending specificity,Base.checked_abs
This function's documentation tells most of the story:
I chose to implement this function only for Signed types, rather than (trivially) making it more generic. I did so, because it struck me as a sort of false advertising to provide this function for other types when it useful only for Signed types. That said, I have no strong feelings against making it more generic.