24
votes

I understand that the meaning of atomic was explained in What's the difference between the atomic and nonatomic attributes?, but what I want to know is:

Q. Are there any side effects, besides performance issues, in using atomic properties everywhere?

It seems the answer is no, as the performance of iPhone is quite fast nowadays. So why are so many people still using non-atomic?

Even atomic does not guarantee thread safety, but it's still better than nothing, right?

2
+1 great question! there's a lot of confusion and misunderstanding around this subject.justin
The official recommendation is to not specify nonatomic unless you truly need to. Note that this does not mean the property is thread-safe. In any case, the general application of this suggestion would be to mark any UI code as nonatomic (since UI code explicitly needs main thread), and leave anything else without that specifier unless you have a good reason (e.g. profiling shows it's a hot path, or you implement your own getter/setter and don't want to guarantee atomic behavior).Lily Ballard
@KevinBallard well, i thought it may not be fruitful to object to that recommendation without hard numbers. so i've done some profiling. hard numbers for uncontested accesses: atomic properties may add 14% on the low end. on the high end, atomic properties can be 24 times slower (twenty-four times). i did not profile contested case. however, it would be astonishing if contested cases were actually faster -- in most cases with spin locks (or any lock in a contested case), the numbers are significantly worse. (cont)justin
(cont) so now i can say "i disagree with the recommendation that atomic should be the default". as well, locating and testing these as hot paths is debatably a fool's errand.justin
@KevinBallard struct t_struct { uint8_t a[3]; }; is one example of a poor performer.justin

2 Answers

34
votes

Even atomic does not guarantee thread safety, but it's still better than nothing, right?

Wrong. Having written some really complex concurrent programs, I recommend exactly the opposite. You should reserve atomic for when it truly makes sense to use -- and you may not fully understand this until you write concurrent programs without any use of atomic. If I am writing a multithreaded program, I don't want programming errors masked (e.g. race conditions). I want concurrency issues loud and obvious. This way, they are easier to identify, reproduce, and correct.

The belief that some thread safety is better than none is flawed. The program is either threadsafe, or it is not. Using atomic can make those aspects of your programs more resistant to issues related to concurrency, but that doesn't buy you much. Sure, there will likely be fewer crashes, but the program is still undisputedly incorrect, and it will still blow up in mysterious ways. My advice: If you aren't going to take the time to learn and write correct concurrent programs, just keep them single threaded (if that sounds kind of harsh: it's not meant to be harsh - it will save you from a lot of headaches). Multithreading and concurrency are huge, complicated subjects - it takes a long time to learn to write truly correct, long-lived programs in many domains.

Of course, atomic can be used to achieve threadsafety in some cases -- but making every access atomic guarantees nothing for thread safety. As well, it's highly unusual (statistically) that atomic properties alone will make a class truly threadsafe, particularly as complexity of the class increases; it is more probable that a class with one ivar is truly safe using atomics only, versus a class with 5 ivars. atomic properties are a feature I use very very rarely (again, some pretty large codebases and concurrent programs). It's practically a corner case if atomics are what makes a class truly thread safe.

Performance and execution complexity are the primary reasons to avoid them. Compared to nonatomic accesses, and the frequency and simplicity of accessing a variable, use of atomic adds up very fast. That is, atomic accesses introduce a lot of execution complexity relative to the task they perform.

Spin locks are one way atomic properties are implemented. So, would you want a synchronization primitive such as a spin lock or mutex implicitly surrounding every get and set, knowing it does not guarantee thread safety? I certainly don't! Making every property access in your implementations atomic can consume a ton of CPU time. You should use it only when you have an explicit reason to do so (also mentioned by dasblinkenlicht+1). Implementation Detail: some accesses do not require spin locks to uphold guarantees of atomic; it depends on several things, such as the architecture and the size of a variable.

So to answer your question "any side-effect?" in a TL;DR format: Performance is the primary reason as you noted, while the applicability of what atomic guarantees and how that is useful for you is very narrow at your level of abstraction (oft misunderstood), and it masks real bugs.

15
votes

You should not pay for what you do not use. Unlike plugged-in computers where CPU cycles cost you in terms of time, CPU cycles on a mobile device cost you both in time and in the battery use. If your application is single-threaded, there is no reason to use atomic, because the locking and unlocking operations would be a waste of time and battery. The battery is more important than the time: while the latency associated with addition of extra operations may be invisible to your end-user, the cycles spent will reduce the time the mobile device can work after a single charge, a measure that a lot of users consider very important.