Maybe you don't need all this information to help me with this problem. The core questions is: What can cause a CADisplayLink
to delay firing, and how to check for possible reasons?
I'm using a CADisplayLink
timer to velocity-/inertia-scroll my own implementation of an audio waveform view:
- (void) startVelocityScrollTimer {
dispatch_sync(dispatch_get_main_queue(), ^{
if (_velocityScrollTimer == nil) {
_velocityScrollTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(velocityScroll)];
_velocityScrollTimer.frameInterval = 1;
[_velocityScrollTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
});
}
I'm experiencing problems with the accuracy of the timer. It usually fires 50 or 60 times a second (not sure, but exact number doesn't matter here). During certain actions, this rate goes down to about 3 to 5 fires per second, which is way too low for smooth scrolling. I am unable to identify what causes these delays.
I've tried:
- Completely uncoupling drawing and loading by outsourcing the loading of audio data to a dedicated dispatch queue, which loads the data asynchronously.
- Profiling and optimizing the drawing method. At peaks, it takes a maximum of 6ms.
- Profiling the method called when the timer fires (velocityScroll:). It usually takes less than 1ms to execute, sometimes peaks are at 3ms.
- using
NSRunLoopCommonModes
instead ofNSDefaultRunLoopMode
for theCADisplayLink
timer
In the caching, I have a lot of dispatch_sync's and a few dispatch_barrier_sync's. Although most of them should be irrelevant, I've
- profiled every dispatch-to-start time in my caching
- profiled every execution time of dispatched blocks in the caching
Although most of them must be irrelevant. I didn't find any waits/blocking which took more than 50ms. This is a bit slow, but still I think it should not get the timer down to 5 fires/second or worse.
For profiling, I've not used a profiling tool (couldn't handle them). Instead, I used the typical approach:
double start = CACurrentMediaTime();
// code to profile here
NSLog(@"it took %.2fms", (CACurrentMediaTime() - start) * 1000);
Here is the method executed when the timer fires. The three methods in the middle are mostly plain simple position-calculations which are executed in almost no time, except the last one, which does a dispatch_sync to the main queue. However, this completes extremely quickly (less then 3ms, see above).
- (void) velocityScroll {
Float32 cyclesPerSecond = 1.0 / _velocityScrollTimer.duration;
[self applyScrollVelocityToScrollPosition:cyclesPerSecond];
[self applyScrollVelocityDecelerationToScrollVelocity:cyclesPerSecond];
[self ensureVelocityScrollOnlyActiveIfNeeded];
[self setNeedsDisplay];
}
I know you guys would like to see more code, but there is a lot of code and I don't want to post all of it. Please ask for individual parts, which I will then post here, if you need them.
What could cause the delays between fires of the CADisplayLink
timer or how to find it out?