プロパティのatomicとnonatomic 三たび
ちょこっと掘り下げてみます
まずソースをちょこっと変えます。変更点だけ。
Test01 *test = [[[aClass alloc] init] autorelease]; test.obj = [[[NSMutableArray alloc] initWithCapacity:1] autorelease]; // ここ変更 id x[2];
objプロパティに入れるインスタンスを@""からNSMutableArrayに変えただけです。これで実行すると
#nonatomic MBP:~/tmp/src masaki$ time ./test 0 real 0m1.195s user 0m1.170s sys 0m0.006s #atomic MBP:~/tmp/src masaki$ time ./test 1 real 0m15.677s user 0m14.125s sys 0m1.294s
なんと12倍に悪化します。
Instrumentsで何がどうなっているか見てみましょう。
-objメッセージは
nonatomic | 134.4ms |
atomic | 9965.0ms |
でatomicが75倍時間が掛かってます。
やっぱりatomicは相当遅いってことですね。
さらに-[NSAutoreleasePool release]にも注目しましょう。
nonatomic | 4899.4ms |
atomic | 11194.8ms |
なんとatomicだと2倍の時間が掛かってます。
これはatomicの合成されたゲッタでは
- (id) obj { return [[obj retain] autorelease]; }
的なことが行われているからです。安全性のためobjがどこか別のスレッドでreleaseされても大丈夫なようにしているのですね。
実際の実装(に近いと思われるもの)はこのへんのobjc_getProperty_non_gc()を見てください。
で、このautoreleaseが@""よりNSMutableArrayで速度が悪化した理由です。
@""(__NSCFConstantStringクラスのインスタンスです)では -retain, -autoreleaseともに自分自身を返すだけで何もしてません。もちろんオートリリースプールにも登録されません。
なので、速度はNSMutableArrayの場合よりも速かったという訳です。
ちなみに
for(unsigned long i = 0; i < 100000000; i++) { id pool01 = nil; if(i%333) pool01 = [[NSAutoreleasePool alloc] init]; x[i%2] = test.obj; [pool01 release]; pool01 = nil; }
こんな感じにすると、
#nonatomic MBP:~/tmp/src masaki$ time ./test 0 real 0m12.690s user 0m12.641s sys 0m0.010s #atomic MBP:~/tmp/src masaki$ time ./test 1 real 0m25.865s user 0m25.340s sys 0m0.040s
となって12倍が2倍になった!と思うけど、始めのとの差は
nonatomic | 11.471ms |
atomic | 11.215ms |
で、単にNSAutoreleasePoolで時間を同じだけ喰っただけで何も変わりません。
もちろん、atomic, nonatomicは安全性を考慮して決定すべきものですので、全部nonatomicにしろとは言いません。言えません。