2

I require a better way of calculating an S-Curve than the below method. I'm using it to draw an S-Curve in a drawRect method as well as calculating the ease-in/ease-out of the volume of a music file for fading.

The reason I need an improved way of doing this is because it gets called roughly 100 times in a loop to calculate the curve and is highly CPU intensive.

I'm hoping that maybe one or a few vector math functions from the accelerate framework might help but I'm not sure where to start.

3 * position * (1 - position) * (1 - position) * firstControlPoint + 3 *
position * position * (1 - position) * secondControlPoint +
position * position * position * 1.0;

Where firstControlPoint equals 0.0 and secondControlPoint equals 1.0.

Anthony Myatt
  • 326
  • 1
  • 12

2 Answers2

1

Your S-curve is a Bezier Curve so you could use the De Casteljau's algorithm.

q0 = t * p0 + (1 - t) * p1
q1 = t * p1 + (1 - t) * p2
q2 = t * p2 + (1 - t) * p3
r0 = t * q0 + (1 - t) * q1
r1 = t * q1 + (1 - t) * q2
s0 = t * r0 + (1 - t) * r1

Then you could use SSE/AVX-intrinsics to compute multiples curves (2 -> 128 bits, 4 -> 256 bits) with a single stream of instructions.

matovitch
  • 1,264
  • 11
  • 26
1

You may be interested in this article on Even Faster Bézier, but 100 calculations of this is not a lot. I've run thousands of such calculations per frame on first-generation iPads. For such a tiny set, you're unlikely to get much benefit from Accelerate (and Accelerate can be much slower than simple C for small data sets).

There are several things to consider, though:

  • If the control points are invariable, you should be able to pre-calculate the values for all positions and stick them in a table. That's going to dramatically improve performance. If they vary less often than you draw, then it's still worth pre-calculating the table whenever the inputs change. Also, make sure that you're not calculating these values more often then you actually need them (if the input values can vary quickly, you may want to wait for the inputs to settle before recalculating anything).

  • If this is an NEON device (i.e an iPhone or iPad), intrinsics are almost never a win (this may change as Clang gets better, but that was my finding in 2012). Hand-coded NEON can definitely be a win if you really need the performance, but it's a headache to program, so I would look everywhere else first. Assembly programming is radically different from C programming. If you could just drop an intrinsic in at a certain point and make it go faster, the compiler would have done that already (and in fact it does).

  • If you're doing floating point math, and you just need the results to be "almost exactly correct and perfect for drawing and animations" rather than "exactly correct and reproducible according to IEEE rules," you should turn on fast math. The easiest way to do that is to switch the compiler optimization from "Fastest, Smallest" to "Fastest, Aggressive Optimizations." It's hard to imagine a case when this is not the correct setting for iOS apps, and it's almost always the correct setting for Mac apps. This setting also turns on additional vectorization, which can make a big difference in your loops.

  • You should definitely look at Optimize Your Code Using LLVM from WWDC 2013. It covers how to structure you code to let the compiler help you the most. You may also want to look at The Accelerate Framework from the same videos, but it's unlikely that Accelerate is the right tool for this problem.

  • Rather than calculating this yourself, consider using a CAPropertyAnimation with a custom timing function. These can be used to set any value; not just layer animations. For drawing the curve, consider using a UIBezierPath rather than hand-calcuating the curve.

For an example of some of this in practice, you may find the CurvyText example from iOS Pushing the Limits to be useful. It calculates both the Bézier points and their slope to perform text layout along a moving curve.

Community
  • 1
  • 1
Rob Napier
  • 286,113
  • 34
  • 456
  • 610