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

Looks cool, but way slower #23

Closed
PallHaraldsson opened this issue Dec 5, 2019 · 13 comments · Fixed by #33
Closed

Looks cool, but way slower #23

PallHaraldsson opened this issue Dec 5, 2019 · 13 comments · Fixed by #33

Comments

@PallHaraldsson
Copy link

PallHaraldsson commented Dec 5, 2019

julia> @btime sin(1octant)
  573.302 ns (0 allocations: 0 bytes)

julia> @btime sinpi(1)
  1.048 ns (0 allocations: 0 bytes)

julia> import UnitfulAngles: °, rad, octant
WARNING: could not import UnitfulAngles.° into Main
WARNING: could not import UnitfulAngles.rad into Main

julia> VERSION
v"1.3.0-alpha.0"

I also wanted to let you know the import from the docs didn't work. I may look into both later, just wanted to file so I don't forget.

@yakir12
Copy link
Owner

yakir12 commented Jan 7, 2020

Thank you for looking into this. I've been very ambivalent about this package for quite some time. On the one hand it's nifty but almost always I end up using radians everywhere and that's that.

The only time I really found a real need for this is when humans IO was involved, in the form of GUI or plots.

@yakir12
Copy link
Owner

yakir12 commented Feb 11, 2020

Just for bookkeeping's sake, we need to:

  1. figure out how to have using UnitfulAngles suffice and work for all our needs, so no need for an additional using Unitful which I think is the case currently. This should also solve the warnings you got.
  2. figure out what's causing the slowdown and solve it.

@MattEttus
Copy link

I think the reason the import statement used in the docs doesn't work is because neither the degree symbol nor rad are defined in this code. They seem to both be defined in unitful itself, so the following works for me:

import Unitful: °, rad

@cmichelenstrofer
Copy link
Collaborator

cmichelenstrofer commented May 4, 2022

#32 fixes the issues with the import statements.

@cmichelenstrofer
Copy link
Collaborator

cmichelenstrofer commented May 4, 2022

I found the culprit of the slowdown! It is the use of rational numbers (with //) in the definitions. E.g. the octant is defined as turn//8. If you change it to turn/8 it is as fast as the baseline trig functions. I was surprised this causes such a slowdown, but it is a known issue (see JuliaLang/julia#11522). I don't think I recommend changing it however for the only reason that Unitful.jl uses rational numbers (//) as well. The degree and radian are already fast and that is what most people use anyway.

defining:

@unit octant        "octant"        Octant         turn//8       false
@unit octant_f      "octant_f"      Octant_f       turn/8        false

Then:

julia> using BenchmarkTools

julia> using UnitfulAngles: °, rad, octant, octant_f

@btime sin(1)
  0.001 ns (0 allocations: 0 bytes)
0.8414709848078965

julia> @btime sin(1rad)
  0.001 ns (0 allocations: 0 bytes)
0.8414709848078965

julia> @btime sin(1°)
  0.001 ns (0 allocations: 0 bytes)
0.01745240643728351

julia> @btime sin(1octant)
  90.691 ns (0 allocations: 0 bytes)
0.7071067811865476

julia> @btime sin(1octant_f)
  0.001 ns (0 allocations: 0 bytes)
0.7071067811865475

@cmichelenstrofer
Copy link
Collaborator

I've been very ambivalent about this package for quite some time. On the one hand it's nifty but almost always I end up using radians everywhere and that's that.

The only time I really found a real need for this is when humans IO was involved, in the form of GUI or plots.

Understood. Sorry for the influx of comments and PRs... 😄 I'm learning Julia and have been using the different packages in the Unitful ecosystem to learn how to write good Julia, write packages, etc. For what it's worth, I think this is a neat little package worth maintaining. Thanks!

@yakir12
Copy link
Owner

yakir12 commented May 4, 2022

Great detective work @cmichelenstrofer !
Adding tons of _f versions for all the cases we use rationals is an option. It would be better if JuliaLang/julia#11522 gets resolved (looks like it stagnated 2 years ago, not sure why).

@cmichelenstrofer
Copy link
Collaborator

cmichelenstrofer commented May 5, 2022

If you use a float rather than an integer then there is no performance issues:

@btime sin(1.0octant)
  0.001 ns (0 allocations: 0 bytes)
0.7071067811865476

Since there is already a way to get fast performance I would say there is no need to duplicate the units with _f versions.

@cmichelenstrofer
Copy link
Collaborator

cmichelenstrofer commented May 5, 2022

This seems to be more an issue with Unitful than with Julia:

julia> @btime(0.125)
  0.001 ns (0 allocations: 0 bytes)
0.125

julia> @btime(1//8)
  0.001 ns (0 allocations: 0 bytes)
1//8

See this comment in PainterQubits/Unitful.jl#281 which points to the offending line in the Unitful code. However this is considered a feature:

the logic is that if the Quantity you start with is represented exactly using a rational or integer Quantity, and the conversion factors can also be expressed exactly, then there should be no error permitted in the conversion, even if converting to a floating-point Quantity.

In that same comment he recommends simply passing in floats for fast code:

I think the solution is probably just to ensure floats are passed in.

Given that this is expected behavior from Unitful, and there is a workaround (using floats), I would say that no change to UnitfulAngles is needed. This really should be documented in Unitful though.

@zsoerenm
Copy link

zsoerenm commented May 5, 2022

You'll need to be careful with timings well below 1ns.
Something like

julia> @btime sin(1rad)
  0.001 ns (0 allocations: 0 bytes)
0.8414709848078965

is completely calculated at the compiler.
If you'd like to measure the runtime performance, you'll have to benchmark the following: @btime sin($(Ref(1rad))[])
Also note that this @btime(1//8) is actually not calculating anything but calling the constructor of fractions.
Here is the runtime performance of a calculation:

julia> @btime $(Ref(1))[] // $(Ref(8))[] * $(Ref(2.1))[]
  13.367 ns (0 allocations: 0 bytes)
0.2625

julia> @btime $(Ref(0.125))[] * $(Ref(2.1))[]
  0.977 ns (0 allocations: 0 bytes)
0.2625

@yakir12
Copy link
Owner

yakir12 commented May 5, 2022

I agree. Since this issue originates elsewhere and shouldn't be resolved out of scope here, I'll close this for now. Feel free to reopen if you disagree.

@yakir12 yakir12 closed this as completed May 5, 2022
@cmichelenstrofer
Copy link
Collaborator

@zsoerenm that is my mistake, I copied the wrong lines from the REPL! I meant to show this:

julia> @btime cos(0.125)
  0.875 ns (0 allocations: 0 bytes)
0.992197667229329

julia> @btime cos(1//8)
  0.875 ns (0 allocations: 0 bytes)
0.992197667229329

Here are some new benchmarks using the method you shared (hopefully I'm using it right). The conclusions above are unchanged.

julia> @btime sin($(Ref(1))[])
  10.917 ns (0 allocations: 0 bytes)
0.8414709848078965

julia> @btime sin($(Ref(1rad))[])
  10.917 ns (0 allocations: 0 bytes)
0.8414709848078965

julia> @btime sin($(Ref(1octant))[])
  95.588 ns (0 allocations: 0 bytes)
0.7071067811865476

julia> @btime sin($(Ref(1.0octant))[])
  6.041 ns (0 allocations: 0 bytes)
0.7071067811865476

julia> @btime sin($(Ref(1//8))[])
  3.041 ns (0 allocations: 0 bytes)
0.12467473338522769

julia> @btime sin($(Ref(0.125))[])
  3.041 ns (0 allocations: 0 bytes)
0.12467473338522769

Also, thank you for pointing out the right way to use @btime in this case! I will read more about how to use BenchmarkTools properly.

@cmichelenstrofer
Copy link
Collaborator

cmichelenstrofer commented May 6, 2022

@yakir12 based on the comment

I think the solution is probably just to ensure floats are passed in.

in the trig functions, before calling uconvert, we can convert the arguments to floats. This is OK since the outputs of the trig functions are not going to be integers or rationals.

current behavior:

julia> @btime sin($(Ref(1octant))[])
  97.251 ns (0 allocations: 0 bytes)
0.7071067811865476

converting to float before uconvert

julia> @btime sin($(Ref(1octant))[])
  5.833 ns (0 allocations: 0 bytes)
0.7071067811865476

I'll submit a PR with this proposed solution.

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

Successfully merging a pull request may close this issue.

5 participants