Skip to content
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

Use roundeven instead of rint for round(x) #40514

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft

Conversation

vchuravy
Copy link
Sponsor Member

The llvm.rint intrinsic is sensitive to the floating-point
environment, so determined enough users can change the definition
of round(x, RoundNearest) to not be rount to even.

julia> round(2.5, RoundNearest)
2.0
julia> Base.Rounding.setrounding_raw(Float64, Base.Rounding.to_fenv(RoundUp))
julia> round(2.5, RoundNearest)
3.0
julia> roundeven(x::Float64) = ccall("llvm.roundeven.f64", llvmcall, Float64, (Float64,), x)
round_even (generic function with 1 method)
julia> roundeven(2.5)
2.0

The intrinsic was introduced with LLVM 11, and roundeven will be part
of the C23 standard (problematic for us due to runtime_intrinsics.cpp).

Sadly performance isn't looking to hot. Using @simonbyrne benchmark from
#8750 (comment)

julia> roundeven_llvm(x::Float64) = ccall("llvm.roundeven.f64", llvmcall, Float64, (Float64,), x)
roundeven_llvm (generic function with 1 method)

julia> rint_llvm(x::Float64) = ccall("llvm.rint.f64", llvmcall, Float64, (Float64,), x)
rint_llvm (generic function with 1 method)

julia> function test_roundeven_llvm(N)
           t = 1.0
           s = 0.0
           for i = 1:N
               t += eps()
               s += roundeven_llvm(t)
           end
           s
       end
test_roundeven_llvm (generic function with 1 method)

julia> function test_rint_llvm(N)
           t = 1.0
           s = 0.0
           for i = 1:N
               t += eps()
               s += rint_llvm(t)
           end
           s
       end
test_rint_llvm (generic function with 1 method)

julia> @time test_roundeven_llvm(100_000_000);
  0.274178 seconds

julia> @time test_roundeven_llvm(100_000_000);
  0.274118 seconds

julia> @time test_rint_llvm(100_000_000);
  0.087601 seconds

julia> @time test_rint_llvm(100_000_000);
  0.087860 seconds

On x86:

julia> @code_native roundeven_llvm(2.5)
	.text
	movabsq	$roundevenf64, %rax
	jmpq	*%rax
	nopl	(%rax)

e.g it's implemented with a function call to roundevenf64, which is implemented by libc.

@mkitti
Copy link
Contributor

mkitti commented Apr 17, 2021

A custom x86 with sse4.1 lowering for llvm.roundeven was added with LLVM 12
https://reviews.llvm.org/D84592
https://reviews.llvm.org/rGc4823b24a41b9e3552af7781e05ed1784fb84f46

@ViralBShah ViralBShah added the maths Mathematical functions label Feb 26, 2022
@ViralBShah
Copy link
Member

@simonbyrne your thoughts here?

The `llvm.rint` intrinsic is sensitive to the floating-point
environment, so determined enough users can change the definition
of `round(x, RoundNearest)` to not be rount to even.

```
julia> round(2.5, RoundNearest)
2.0
julia> Base.Rounding.setrounding_raw(Float64, Base.Rounding.to_fenv(RoundUp))
julia> round(2.5, RoundNearest)
3.0
julia> round_even(x::Float64) = ccall("llvm.roundeven.f64", llvmcall, Float64, (Float64,), x)
round_even (generic function with 1 method)
julia> round_even(2.5)
2.0
```
@simonbyrne
Copy link
Contributor

Since we're now using a newer LLVM, this should be fine, and we should probably make the change.
I tried rebasing, but not sure how to deal with runtime intrinsics: @vchuravy?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
maths Mathematical functions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants