TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Speeding up atan2f

245 pointsby rostayobalmost 4 years ago

21 comments

gumbyalmost 4 years ago
Because of the disregard for the literature common in CS I loved this part:<p>&gt; This is achieved through ...<i>and some cool documents from the 50s</i>.<p>A bit of anecdote: back when I was a research scientist (corporate lab) 30+ years ago I would in fact go downstairs to the library and read — I was still a kid with a lot to learn (and still am). When I came across (by chance or by someone’s suggestion) something useful to my work I’d photocopy the article and and try it out. I’d put a comment in the code with the reference.<p>My colleagues in my group would give me (undeserved) credit for my supposed brilliance even though I said in the code where the idea came from and would determinedly point to the very paper on my desk. This attitude seemed bizarre as the group itself was producing conference papers and even books.<p>(Obviously this was not a universal phenomenon as there were other people in the lab overall, and friends on the net, suggesting papers to read. But I’ve seen it a lot, from back then up to today)
评论 #28211018 未加载
评论 #28210110 未加载
评论 #28210819 未加载
评论 #28210111 未加载
评论 #28212196 未加载
评论 #28213387 未加载
评论 #28215545 未加载
评论 #28214116 未加载
jacobolusalmost 4 years ago
If someone wants a fast version of <i>x</i> ↦ tan(<i>πx</i>&#x2F;2), let me recommend the approximation:<p><pre><code> tanpi_2 = function tanpi_2(x) { var y = (1 - x*x); return x * (((-0.000221184 * y + 0.0024971104) * y - 0.02301937096) * y + 0.3182994604 + 1.2732402998 &#x2F; y); } </code></pre> (valid for -1 &lt;= x &lt;= 1)<p><a href="https:&#x2F;&#x2F;observablehq.com&#x2F;@jrus&#x2F;fasttan" rel="nofollow">https:&#x2F;&#x2F;observablehq.com&#x2F;@jrus&#x2F;fasttan</a> with error: <a href="https:&#x2F;&#x2F;www.desmos.com&#x2F;calculator&#x2F;hmncdd6fuj" rel="nofollow">https:&#x2F;&#x2F;www.desmos.com&#x2F;calculator&#x2F;hmncdd6fuj</a><p>But even better is to avoid trigonometry and angle measures as much as possible. Almost everything can be done better (faster, with fewer numerical problems) with vector methods; if you want a 1-float representation of an angle, use the stereographic projection:<p><pre><code> stereo = (x, y) =&gt; y&#x2F;(x + Math.hypot(x, y)); stereo_to_xy = (s) =&gt; { var q = 1&#x2F;(1 + s*s); return !q ? [-1, 0] : [(1 - s*s)&#x2F;q, 2*s&#x2F;q]; }</code></pre>
评论 #28212698 未加载
aj7almost 4 years ago
Around 1988, I added phase shift to my optical thin film design program written in Excel 4.0 for the Mac. At the time, this was utterly unique: each spreadsheet row represented a layer and the matrices describing each layer could be calculated right in that row by squashing them down horizontally. The S- and P-polarization matrices could be recorded this way, and the running matrix products similarly maintained. Finally, using a simple one input table, the reflectance of a typically 25-31 layer laser mirror could be calculated. And in less than a second on a 20 MHz 68020 (?) Mac II for about 50 wavelengths. The best part were the graphics which were instantaneous, beautiful, publishable, and pasteable into customer quotations. Semi-technical people could be trained to use the whole thing.<p>Now about the phase shift. In 1988, atan2 didn’t exist. Anywhere. Not in FORTRAN, Basic, Excel, or a C library. I’m sure phase shift calculators implemented it, each working alone. For us, it was critical. You see we actually cared not about the phase shift, but its second derivative, the group delay dispersion. This was the beginning of the femtosecond laser era, and people needed to check whether these broadband laser pulses would be inadvertently stretched by reflection off or transmission through the mirror coating. So atan2, the QUADRANT-PRESERVING arc tangent, is required for a continuous,differential phase function. An Excel function macro did this, with IF statements correcting the quadrant. And the irony of all this?<p>I CALLED it atan2.
评论 #28213813 未加载
评论 #28212917 未加载
评论 #28214883 未加载
drejalmost 4 years ago
Nice. Reminds me of an optimisation trick from a while ago: I remember being bottlenecked by one of these trigonometric functions years ago when working with a probabilistic data structure... then I figured the input domain was pretty small (a couple dozen values), so I precomputed those and used an array lookup instead. A huge win in terms of perf, obviously only applicable in these extreme cases.
评论 #28209677 未加载
评论 #28211653 未加载
评论 #28209650 未加载
Const-mealmost 4 years ago
I wonder how does it compare with Microsoft’s implementation, there: <a href="https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;DirectXMath&#x2F;blob&#x2F;jan2021&#x2F;Inc&#x2F;DirectXMathVector.inl#L4915-L5104" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;DirectXMath&#x2F;blob&#x2F;jan2021&#x2F;Inc&#x2F;Di...</a><p>Based on the code your version is probably much faster. It would be interesting to compare precision still, MS uses 17-degree polynomial there.
评论 #28210078 未加载
stephencanonalmost 4 years ago
&gt; if we’re working with batches of points and willing to live with tiny errors, we can produce an atan2 approximation which is 50 times faster than the standard version provided by libc.<p>Which libc, though? I assume glibc, but it&#x27;s frustrating when people talk about libc as though there were a single implementation. Each vendor supplies their own implementation, libc is just a common interface defined by the C library. There is no &quot;standard version&quot; provided by libc.<p>In particular, glibc&#x27;s math functions are not especially fast--Intel&#x27;s and Apple&#x27;s math libraries are 4-5x faster for some functions[1], and often more accurate as well, for example (and both vendors provide vectorized implementations). Even within glibc versions, there have been enormous improvements over the last decade or so, and for some functions there are big performance differences depending on whether or not -fno-math-errno is specified. (I would also note that atan2 has a lot of edge cases, and more than half the work in a standards-compliant libc is in getting those edge cases with zeros and infinities right, which this implementation punts on. There&#x27;s nothing wrong with that, but that&#x27;s a bigger tradeoff for most users than the small loss of accuracy and important to note.)<p>So what are we actually comparing against here? Comparing against a clown-shoes baseline makes for eye-popping numbers, but it&#x27;s not very meaningful.<p>None of this should really take away from the work presented, by the way--the techniques described here are very useful for people interested in this stuff.<p>[1] I don&#x27;t know the current state of atan2f in glibc specifically; it&#x27;s possible that it&#x27;s been improved since last I looked at its performance. But the blog post cites &quot;105.98 cycles &#x2F; element&quot;, which would be glacially slow on any semi-recent hardware, which makes me think something is up here.
评论 #28214937 未加载
评论 #28215006 未加载
hderschalmost 4 years ago
A comparison with one of the many SIMD-mathlibraries would have been fairer than with plain libm. Long time ago I wrote such a dual-platform library for the PS3 (cell-processor) and x86 architecture (outdated, but still available here [1]). Depending on how standard libm implements atan2f, a speedup of 3x to 15x is achieved, without sacrifying accuracy.<p>1. <a href="https:&#x2F;&#x2F;webuser.hs-furtwangen.de&#x2F;~dersch&#x2F;libsimdmath.pdf" rel="nofollow">https:&#x2F;&#x2F;webuser.hs-furtwangen.de&#x2F;~dersch&#x2F;libsimdmath.pdf</a>
prionassemblyalmost 4 years ago
I wonder whether Padé approximants are well known by this kind of researcher. E.g. <a href="http:&#x2F;&#x2F;www-labs.iro.umontreal.ca&#x2F;~mignotte&#x2F;IFT2425&#x2F;Documents&#x2F;EfficientApproximationArctgFunction.pdf" rel="nofollow">http:&#x2F;&#x2F;www-labs.iro.umontreal.ca&#x2F;~mignotte&#x2F;IFT2425&#x2F;Documents...</a>
评论 #28210492 未加载
jvz01almost 4 years ago
I have developed very fast, accurate, and vectorizable atan() and atan2() implementations, leveraging AVX&#x2F;SSE capabilities. You can find them here [warning: self-signed SSL-Cert].<p><a href="https:&#x2F;&#x2F;fox-toolkit.org&#x2F;wordpress&#x2F;?p=219" rel="nofollow">https:&#x2F;&#x2F;fox-toolkit.org&#x2F;wordpress&#x2F;?p=219</a>
评论 #28212094 未加载
评论 #28211829 未加载
drfuchsalmost 4 years ago
Wouldn’t CORDIC have done the trick faster? There’s no mention that they even considered it, even though it’s been around for half a century or so.
评论 #28209778 未加载
评论 #28209836 未加载
zokieralmost 4 years ago
I would have wished to see the error analysis section expanded a bit, or maybe seeing some sort of tests to validate the max error. In particular if the mathematical approximation function arctan* has max error of 1&#x2F;10000 degrees then I&#x27;d naively expect that the float-based implementation to have worse error. Furthermore it&#x27;s not obvious if additional error could be introduced by the division<p><pre><code> float atan_input = (swap ? x : y) &#x2F; (swap ? y : x);</code></pre>
shooalmost 4 years ago
Related -- there&#x27;s a 2011 post from Paul Minero with fast approximations for logarithm, exponential, power, inverse root. <a href="http:&#x2F;&#x2F;www.machinedlearnings.com&#x2F;2011&#x2F;06&#x2F;fast-approximate-logarithm-exponential.html" rel="nofollow">http:&#x2F;&#x2F;www.machinedlearnings.com&#x2F;2011&#x2F;06&#x2F;fast-approximate-lo...</a><p>Minero&#x27;s faster approximate log2, &lt; 1.4% relative error for x in [1&#x2F;100, 10]. Here&#x27;s the simple non-sse version:<p><pre><code> static inline float fasterlog2 (float x) { union { float f; uint32_t i; } vx = { x }; float y = vx.i; y *= 1.1920928955078125e-7f; return y - 126.94269504f; } </code></pre> This fastapprox library also includes fast approximations of some other functions that show up in statistical &#x2F; probabilistic calculations -- gamma, digamma, lambert w function. It is BSD licensed, originally lived in google code, copies of the library live on in github, e.g. <a href="https:&#x2F;&#x2F;github.com&#x2F;etheory&#x2F;fastapprox" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;etheory&#x2F;fastapprox</a><p>It&#x27;s also interesting to read through libm. E.g. compare Sun&#x27;s ~1993 atan2 &amp; atan:<p><a href="https:&#x2F;&#x2F;github.com&#x2F;JuliaMath&#x2F;openlibm&#x2F;blob&#x2F;master&#x2F;src&#x2F;e_atan2.c" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;JuliaMath&#x2F;openlibm&#x2F;blob&#x2F;master&#x2F;src&#x2F;e_atan...</a><p><a href="https:&#x2F;&#x2F;github.com&#x2F;JuliaMath&#x2F;openlibm&#x2F;blob&#x2F;master&#x2F;src&#x2F;s_atan.c" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;JuliaMath&#x2F;openlibm&#x2F;blob&#x2F;master&#x2F;src&#x2F;s_atan...</a>
pklausleralmost 4 years ago
(Undoubtedly) stupid question: would it be any faster to project (x, y) to the unit circle (x&#x27;, y&#x27;), then compute acos(x&#x27;) or asin(y&#x27;), and then correct the result based on the signs of x &amp; y? When converting Cartesian coordinates to polar, the value of r=HYPOT(x, y) is needed anyway, so the projection to the unit circle would be a single division by r.
sorenjanalmost 4 years ago
How do you handle arrays of values where the array lengths are not a multiple of 8 in this kind of vectorized code? Do you zero pad them before handling them to the vectorized function, or do you run a second loop element by element on the remaining elements after the main one? What happens if you try to do `_mm256_load_ps(&amp;ys[i])` with &lt; 8 elements remaining?
评论 #28210632 未加载
评论 #28211150 未加载
评论 #28210868 未加载
评论 #28210597 未加载
aconz2almost 4 years ago
Nice writeup and interesting results. I hadn&#x27;t seen the use of perf_event_open(2) before directly in code which looks cool.<p>The baseline is at a huge disadvantage here because the call to atan2 in the loop never gets inlined and the loop doesn&#x27;t seem to get unrolled (which is surprising actually). Manually unrolling by 8 gives me an 8x speedup. Maybe I&#x27;m missing something with the `-static` link but unless they&#x27;re using musl I didn&#x27;t think -lm could get statically linked.
评论 #28211610 未加载
评论 #28214007 未加载
nice2meetualmost 4 years ago
I did something similar for tanh once, though I found I could get to 1 ulp.<p>Part of the motivation was that I could get 10x faster than libc. However, I then tried on my FreeBSD and could only get 4x faster. After a lot of head scratching and puzzling it turned out there was a bug in the version of libc on my linux box that slowed things down. It kind of took the wind out of the achievement, but it was still a great learning experience.
azhenleyalmost 4 years ago
This is pretty similar to my quest to make my own cos() when my friend didn&#x27;t have access to libc. It was fun! Though I don&#x27;t have the math or low-level knowledge that this author does.<p><a href="https:&#x2F;&#x2F;web.eecs.utk.edu&#x2F;~azh&#x2F;blog&#x2F;cosine.html" rel="nofollow">https:&#x2F;&#x2F;web.eecs.utk.edu&#x2F;~azh&#x2F;blog&#x2F;cosine.html</a>
spaetzleesseralmost 4 years ago
I am envious of people who can deal with such problems. The problem is defined clearly and can be measured easily.<p>This is so much more fun than figuring out why some SAAS service is misbehaving.
cogman10almost 4 years ago
I&#x27;m actually a bit surprised that the x86 SIMD instructions don&#x27;t support trig functions.
h0miealmost 4 years ago
Love posts like this!
unemphysbroalmost 4 years ago
coolest blog post I&#x27;ve seen here in a while. :)