I develop a method where each 3 seconds I show a image moving. I make with dispatch_after, but in each execution of the app, the distance or the time when the image is shown it's diferent. Is more efficient use an NSTimer with a Schedule??
Yes, NSTimer is high level API and easy to implement and control like if you want to pause or stop that animation you can do it. But GCD is better in performance as I know.
I think for a repeating time period NSTimer is the more obvious choice. Apart from anything else you need to make sure that one and exactly one dispatch_after is called for each time you get called.
With a dispatch_after chain there may be a risk that there will be a slight drift in timing as delays add up where with the NSTimer if one event is slightly late there should be a fractionally shorter delay to the next one so that it doesn't suffer drift over time.
Related
In a Gtk::DrawingArea I have a pixbuf showing the layout of my house. I draw the measured room temperatures on it. I also would like to draw the state of my shutters on it with some lines. When and only when a shutter changes its state, I would love to make these lines blink with a time offset of 1 second. I assume, I would have to make use of a timeout triggered every second to redraw the lines for the shutters. I am already making use of a timeout every 2 minutes to fetch new data from the internet to be shown on my screen. I could set up the timeout to get called every second and then I would have to remember, when my last 2-minute fetch was accomplished, to trigger the next one on time. Also, if my shutters are not changing state like in 99.9 percent of their lifetime, I do not need blinking. It feels over engineered to me to call a method every second just to make a line blink. Is there a smarter way to do this?
I could post a lot of code here, but I think that would not help anybody understand my question. I am helpful for any hint.
I am working on a Sprite Kit game and I need to do some multithreading to maintain the healthy fps.
On update I call a function to create a lot of UIBezierPaths and merge them using a C++ static library.
If I have more than 10 shapes the frame rate drops dramatically so I decided I give GCD a try and try to solve the issue with a separate thread.
I put this in didMoveToView:
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
and in the function that is being called on every frame, I call this:
dispatch_async(queue,^(void){[self heavyCalculationsFunc];});
For somebody who knows GCD well it might be obvious it creates a new thread on every frame, but for me it wasn't clear yet.
My question is, is there any way to re-use a thread that I want to call on update?
Thanks for your help in advance!
If you have work that you need to do on every frame, and that needs to get done before the frame is rendered, multithreading probably won't help you, unless you're willing to put a lot of effort into it.
Maintaining a frame rate is all about time — not CPU resources, just wall time. To keep a 60fps framerate, you have 16.67 ms to do all your work in. (Actually, less than that, because SpriteKit and OpenGL need some of that time to render the results of your work.) This is a synchronous problem — you have work, you have a specific amount of time to do it in, so the first step to improving performance is to do less work or do it more efficiently.
Multithreading, on the other hand, is generally for asynchronous problems — there's work you need to do, but it doesn't need to get done right now, so you can get on with the other things you need to do right now (like returning from your update method within 16 ms to keep your framerate up) and check back for the results of that work later (say, on a later frame).
There is a little bit of wiggle room between these two definitions, though: just about all modern iOS devices have multicore CPUs, so if you play your cards right you can fit a little bit of asynchronicity into your synchronous problem by parallelizing your workload. Getting this done, and doing it well, is no small feat — it's been the subject of serious research and investment by big game studios for years.
Take a look at the figure under "How a Scene Processes Frames of Animation" in the SpriteKit Programming Guide. That's your 16 ms clock. The light blue regions are slices of that 16 ms that Apple's SpriteKit (and OpenGL, and other system frameworks) code is responsible for. The other slices are yours. Let's unroll that diagram for a better look:
If you do too much work in any of those slices, or make SpriteKit's workload too large, the whole thing gets bigger than 16 ms and your framerate drops.
The opportunity for threading is to get some work done on the other CPU during that same timeline. If SpriteKit's handling of actions, physics, and constraints doesn't depend on that work, you can do it in parallel with those things:
Or, if your work needs to happen before SpriteKit runs actions & physics, but you have other work you need to do in the update method, you can send some of the work off to another thread while doing the rest of your update work, then check for results while still in your update method:
So how to accomplish these things? Here's one approach using dispatch groups and the assumption that actions/physics/constraints don't depend on your background work — it's totally off the top of my head, so it may not be the best. :)
// in setup
dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t resultCatchingGroup = dispatch_group_create();
id stuffThatGetsMadeInTheBackground;
- (void)update:(NSTimeInterval)currentTime {
dispatch_group_async(group, queue, ^{
// Do the background work
stuffThatGetsMadeInTheBackground = // ...
});
// Do anything else you need to before actions/physics/constraints
}
- (void)didFinishUpdate {
// wait for results from the background work
dispatch_group_wait(resultCatchingGroup, DISPATCH_TIME_FOREVER);
// use those results
[self doSomethingWith:stuffThatGetsMadeInTheBackground];
}
Of course, dispatch_group_wait will, as its name suggests, block execution to wait until your background work is done, so you still have that 16ms time constraint. If the foreground work (the rest of your update, plus SpriteKit's actions/physics/constraints work and any of your other work that gets done in response to those things) gets done before your background work does, you'll be waiting for it. And if the background work plus SpriteKit's rendering work (plus whatever you do in update before spawning the background work) takes longer than 16 ms, you'll still drop frames. So the trick to this is knowing your workload in enough detail to schedule it well.
Consider a slightly different approach. Create and maintain your own queue rather than getting a system queue.
a) call dispatch_queue_create to make a new queue, save this queue in your object. Use dispatch_async on that queue to run your job. You may need to synchronize if that has to complete before the next frame, etc.
b) If you have multiple jobs, consider a concurrent queue instead of a serial queue, which may or may not make things 'faster' depending on your dependencies.
With GCD you're supposed to not think about threads, if new threads are created/reused etc. Just think about the queues, and what you're pushing on to them. Reading Apple's Concurrency Programming Guide and reference on gcd will also hopefully help.
I'm trying to implement a countdown feature for my program. It's a second-timer, so I use a NSTimer object with a time interval of 1.0 second to update the UI. But in order not to accumulate error (every 1.0-second interval will incur a little bit of lag), the program caculates the absolute difference between current time and beginning time for the remaining time displayed in the UI.
The problem is, after the NSTimer object runs for a significant time (say half an hour), it's no longer "synced" with the absolute time due to accumulated error: the UI update happens between two "absolute" seconds. For example, if the countdown starts at 00:00:00.000, at first UI updates at 00:00:01.000, 00:00:02.000 ... but after a while it becomes 00:30:03.567 or something like that.
Any idea how I can deal with this? Are there any other better ways to implement this? Thanks!
One high level idea is to detect when the timer is getting too far out of sync based on your absolute time calculation. When it gets past a specific threshold, say 0.01 seconds or whatever you desire, cancel the current timer and start a new one after an appropriate delay that gets it back "in sync".
I have an app where I flash words at a constant speed. Say it's set to 60 times a minute. Each word then shows for 1 second each. It was pretty easy to accomplish with NSTimer.
However, I want to make it a little more intelligent now. Longer words show for slightly longer than shorter words. I've figured out the math on how to calculate this, but I'm not sure how in Objective-C to present a word for say, 0.15 seconds, then another word for 0.18 seconds, then a third word for 0.04 seconds, etc., depending on the length of the word.
Would just using a delay be the best way to do this?
You could use performSelector to delay, but it isn't necessarily very easy to manage.
You could use NSTimer, repeating, and set the fireDate for each new update required. This is relatively expensive but less so than repeatedly creating new timers.
You could use CADisplayLink with a combination of duration and frameInterval to get updates at multiples of the screen refresh rate. This should probably be the most performant and accurate.
But, overall, you shouldn't worry about performance until you have some evidence of a problem and / or have done some profiling. Think instead about what features you need and how easy they are to implement with each solution.
It seems like the highest timer rate on iOS is 60 times per second using CADisplayLink, which is 0.01666 second each time. Can two timers be set up at the same time, so that an image can be updated more frequently for its location?
Or, can one timer event handler fire off another timer event 0.00833 second later, so that there is another update in between the 1/60 intervals, to achieve 120 fps?
Why would you want to achieve 120 fps on a screen that updates 60 times a second?
That would just waste CPU and power. Don't do it.
However, having a timer that fires more often is definitely possible.
Drawing in each of these callbacks is, however, is not possible. (at least using -drawRect:, don't know about OpenGL).
You can't do this for a number of reasons. First, there's a hard cap at 60fps due to hardware limitations. Second, NSTimer is not nearly accurate enough for this sort of thing. From the Timer Programming Topics - Timer documentation (bold added for emphasis):
Timing Accuracy
A timer is not a real-time mechanism; it fires only when one of the
run loop modes to which the timer has been added is running and able
to check if the timer’s firing time has passed. Because of the various
input sources a typical run loop manages, the effective resolution of
the time interval for a timer is limited to on the order of 50-100
milliseconds. If a timer’s firing time occurs while the run loop is in
a mode that is not monitoring the timer or during a long callout, the
timer does not fire until the next time the run loop checks the timer.
Therefore, the actual time at which the timer fires potentially can be
a significant period of time after the scheduled firing time.