-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
hardware independent isinf/isnan/isfinite #19008
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
Conversation
base/float.jl
Outdated
| isfinite(x::Integer) = true | ||
|
|
||
| isinf(x::Real) = !isnan(x) & !isfinite(x) | ||
| isinf(x::Real) = (reinterpret(Unsigned, x) & ~sign_mask(T)) == exponent_mask(T) |
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.
This is fine for specific floating-point formats (Float16, Float32, Float64) but not ok for generic Reals. I would leave the existing method for Real and add a more specific method for those types.
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.
Good point, I'll do that.
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.
Perhaps we also need an extra IEEEFloat abstract type, for which these things are valid?
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.
@vchuravy has been using IntrinsicFloats
Either sound reasonable.
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 like IEEEFloat, and will use it instead of my IntrinsicFloats.
|
Sounds good for the |
vchuravy
left a comment
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.
Do we have benchmarks for this?
| isinf{T<:IEEEFloat}(x::T) = (reinterpret(Unsigned, x) & ~sign_mask(T)) == exponent_mask(T) | ||
| isinf(x::Real) = !isnan(x) & !isfinite(x) | ||
|
|
||
|
|
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.
unnecessary newline
| Test whether a floating point number is not a number (NaN). | ||
| """ | ||
| isnan{T<:IEEEFloat}(x::T) = (reinterpret(Unsigned, x) & ~sign_mask(T)) > exponent_mask(T) |
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.
Cool did not know reinterpret(Unsigned, x) is a thing.
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.
Is this more efficient than x != x?
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.
It is not
# x != x
julia> @code_native isnan(NaN)
.text
Filename: float.jl
pushq %rbp
movq %rsp, %rbp
Source line: 355
ucomisd %xmm0, %xmm0
setp %al
popq %rbp
retq
nopl (%rax)
# This PR
julia> @code_native isnan2(NaN)
.text
Filename: REPL[3]
pushq %rbp
movq %rsp, %rbp
Source line: 1
movd %xmm0, %rax
movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF
andq %rax, %rcx
movabsq $9218868437227405312, %rax # imm = 0x7FF0000000000000
cmpq %rax, %rcx
seta %al
popq %rbp
retq
nopl (%rax,%rax)I don't think we should sacrifice losing this hardware instructions when available.
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.
See https://gist.github.com/musm/8150451b04120ef27974d8270dedbf6f
for more comprehensive tests (the gist is we are comparing performance in the 10-100 picosecond range...) So basically these instructions are so cheap and fast that it really doesn't matter on modern processors. In other words, the changes are just as fast as what we already have.
For isnan I agree, unfortunately we still require special handling for Float16 which prompted this pr, which I thought would be good to unify all the exceptional case handling to be independent of floating point hardware instructions so that Float16 (F128/F80) would be handled all the same. But the problem currently is that cpu support for small floats is not great (but I think now intel is investing more on this side due to demand).
I'll leave this to @vchuravy expertise on the issue on whether these proposed changes are positive or not, for now I am closing this.
For the denormals even though x - x will not work. It's only on the x87 chips where exceptional handling are very bad (some x87 sandybridge too?), where one may want to disable or flush them to zero.
But disabling denormals is not recommended, so the issue will probably never crop up; someone would have to go out of their way to disable them in Julia anyways.
So perhaps only for isinf and isfinite the changes are positive, but at that point might as well just use what we already have.
|
It seems we do have benchmarks https://github.com/JuliaCI/BaseBenchmarks.jl/blob/0e13bcd0e14337cfb6f6513a1d3d04b468b2abaa/src/scalar/ScalarBenchmarks.jl#L14, even though we should probably bench @nanosoldier |
| isnan(x::Real) = false | ||
|
|
||
| """ | ||
| isfinite(f) -> Bool |
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.
should this be removed from helpdb/Base.jl if it's also there and now duplicated?
| Test whether a floating point number is an infinity value (positive infinity or negative infinity). | ||
| """ | ||
| isinf{T<:IEEEFloat}(x::T) = (reinterpret(Unsigned, x) & ~sign_mask(T)) == exponent_mask(T) |
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.
Again, is this more efficient?
|
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @jrevels |
|
@vchuravy How meaningful is to benchmark changes that are within a fraction of a nanosecond? Apparently master branch of BenchmarkTools is now reliable to within ns range (but perhaps nanosoldier is not yet using the master branch). See: https://gist.github.com/musm/8150451b04120ef27974d8270dedbf6f |
|
I think Nanosoldier might still not use nanosecond resolution. Nevertheless changes to these kinds of function can have effects throughout the whole codebase |
Would be good to have actual examples of such CPUs before making this method more complex. |
|
right, I highly doubt the second point...unless perhaps some ancient cpu that julia doesn't work on :) |
|
Just to clarify: BenchmarkTools/Nanosoldier has always had nanosecond resolution, so if that's what you require here, you've already got it. A recent PR has given BenchmarkTools picosecond resolution, but that hasn't been tagged/released yet because it breaks JLD (de)serialization compatibility with old data (i.e. all the data in BaseBenchmarkReports). |
|
@jrevels On the tagged version of |
We can get rid of the custom
Float16handling of this and have allisinfbe hardware independent.Same thing with isnan ....