Fast sine/cosine for ARMv7+NEON: looking for testers…
Asked Answered
H

2

15

Could somebody with access to an iPhone 3GS or a Pandora please test the following assembly routine I just wrote?

It is supposed to compute sines and cosines really really fast on the NEON vector FPU. I know it compiles fine, but without adequate hardware I can't test it. If you could just compute a few sines and cosines and compare the results with those of sinf() and cosf() it would really help.

Thanks!

#include <math.h>

/// Computes the sine and cosine of two angles
/// in: angles = Two angles, expressed in radians, in the [-PI,PI] range.
/// out: results = vector containing [sin(angles[0]),cos(angles[0]),sin(angles[1]),cos(angles[1])]
static inline void vsincos(const float angles[2], float results[4]) {
    static const float constants[]  = { 
    /* q1 */  0,                M_PI_2,           0,                M_PI_2,
    /* q2 */  M_PI,             M_PI,             M_PI,             M_PI,
    /* q3 */  4.f/M_PI,         4.f/M_PI,         4.f/M_PI,         4.f/M_PI,
    /* q4 */ -4.f/(M_PI*M_PI), -4.f/(M_PI*M_PI), -4.f/(M_PI*M_PI), -4.f/(M_PI*M_PI),
    /* q5 */  2.f,              2.f,              2.f,              2.f,
    /* q6 */  .225f,            .225f,            .225f,            .225f
    };  
    asm volatile(
        // Load q0 with [angle1,angle1,angle2,angle2]
        "vldmia %1, { d3 }\n\t"
        "vdup.f32 d0, d3[0]\n\t"
        "vdup.f32 d1, d3[1]\n\t"
        // Load q1-q6 with constants
        "vldmia %2, { q1-q6 }\n\t"
        // Cos(x) = Sin(x+PI/2), so
        // q0 = [angle1, angle1+PI/2, angle2, angle2+PI/2]
        "vadd.f32 q0,q0,q1\n\t"
        // if angle1+PI/2>PI, substract 2*PI
        // q0-=(q0>PI)?2*PI:0
        "vcge.f32 q1,q0,q2\n\t"
        "vand.f32 q1,q1,q2\n\t"
        "vmls.f32 q0,q1,q5\n\t"
        // q0=(4/PI)*q0 - q0*abs(q0)*4/(PI*PI)
        "vabs.f32 q1,q0\n\t"
        "vmul.f32 q1,q0,q1\n\t"
        "vmul.f32 q0,q0,q3\n\t"
        "vmul.f32 q1,q1,q4\n\t"
        "vadd.f32 q0,q0,q1\n\t"
        // q0+=.225*(q0*abs(q0) - q0)
        "vabs.f32 q1,q0\n\t"
        "vmul.f32 q1,q0,q1\n\t"
        "vsub.f32 q1,q0\n\t"
        "vmla.f32 q0,q1,q6\n\t"
        "vstmia %0, { q0 }\n\t"
        :: "r"(results), "r"(angles), "r"(constants)
        : "memory","cc","q0","q1","q2","q3","q4","q5","q6"
    );  
}
Harpist answered 6/12, 2009 at 3:30 Comment(3)
Just curious -- what algorithm are you using to fast-compute the sin,cos?Calamity
if you add a test program I can run it on my beagleboard.. Same CPU.Insessorial
@gahooa: I use the method described by Nicolas Capens on devmaster.net/forums/showthread.php?t=5784 and the fact that cos(x)=sin(x+90°)Harpist
I
10

Just tested it on my beagleboard.. As said in the comments: Same CPU.

Your code is roughly 15 times faster than the clib.. Well done!

I've measured 82 cycles for each call of your implementation and 1260 for the four c-lib calls. Note that I've compiled with soft-float ABI and my OMAP3 is early silicon, so each call to the the c-lib version has a NEON stall of at least 40 cycles.

I've zipped together the results..

http://torus.untergrund.net/code/sincos.zip

The performance-counter stuff will most likely not work on the iphone.

Hope that's what you've been looking for.

Insessorial answered 6/12, 2009 at 20:5 Comment(6)
Thank you very much Nils. I'm a bit surprised it worked out of the box, actually :-) The same method implemented for the VFP11 is only about twice as fast as calling sinf()+cosf() on my iPod Touch, so I used a lookup table instead.Harpist
Ah I see from your test program that you used the double precision variant of the libc function (sin()/cos(), not sinf()/cosf()). That explains why the libc functions performed so poorly I think :-)Harpist
Just compiled and run it with sinf/cosf, and it does not make much of a difference.Insessorial
Do you mean, there is almost no difference in results compared to when testing with sin()/cos(), versus sinf()/cosf(), or there is almost no difference in speed between my function and libc's?Harpist
There is not much difference between sin and sinf (same for cos). I measure 1260 cycles/interation for the double-version and 1245 cycles/iteration for the float version. I bet it's the same code and we just save the cast from double to float.Insessorial
Using soft-float when compiling is like running it without floating point hardware. All floating point operations are simulated with integer arithmetics instructions, which would give you probably 10x worse performance.Finished
I
3

Oh - before I forget it: Maybe you can safe yourself a bit of work..

Take a look at these NEON optimized math functions:

http://code.google.com/p/math-neon/

Insessorial answered 6/12, 2009 at 20:16 Comment(2)
Yes I know these functions. I wish I had an iPhone 3GS for playing with NEON, which surely is much more fun than working on my iPod's VFP11. With the iPhone a FPU craze started, but before that the rule was: DON'T do floats, DO fixed point. I'm starting to think the guys coding with floats for the iPhone are wrong. ARM is really good with integers, and setting up float registers has a large overhead.Harpist
In fact I know another related project, not this one. The one I had in mind had only matrix/vector functions. Just checked the one you put a link to. It looks like it has all the math.h functions! I'm starring it, thanks! :-)Harpist

© 2022 - 2024 — McMap. All rights reserved.