プロパティの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で何がどうなっているか見てみましょう。


nonatomic


atomic


-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にしろとは言いません。言えません。