|
HMDT - Special Issue - Objective-C 最適化 - 並行プログラミングの値の更新の問題 |
|
以下のドキュメントは、Obj-C Optimization: Concurrent Programming, a quick refresher を翻訳したものです。Mac OS X でのObjective-C の最適化手法について述べられています。 |
|
Mulle kybernetiK - Tech Info: v0.2 Obj-C 最適化:並行プログラミングの値の更新の問題
(c) 2002 Mulle kybernetiK - text by Nat!
並行プログラミングの、値の更新の問題知っての通り、共有されている変更可能なデータは、ロックを使って保護しないといけないんだ。Cocoa/TasksAndConcepts/ProgrammingTopics/Multithreading で説明されているようにな。こんな風な、グローバル変数を増加することでさえ、
ぜんぜんスレッドセーフじゃないんだ。この、1 行の C の命令は、実際には 3 つかそれ以上の PPC の命令なんだ。これに似てるか、そのものか何だけど: lwz r11,0(r9) addi r0,r11,1 stw r0,0(r9) それぞれの PPC 命令が実行されるときに、割り込みが発生する可能性があるんだ。このスレッドの実行が中断されて、プロセッサの興味が他のスレッドに向いてしまう。この別のスレッドは、同じグローバル変数にアクセスするために、同じコードを実行することがあるんだ! これがどんな問題を引き起こすか、2 つのスレッドが同じコードを実行する、2 つのケースを見てみよう。最初の例では、2 番目のスレッドへのコンテキストスイッチは、問題がない。値の増加はすでに終わっているからな:
だけど、これもよく起きるんだけど、コンテキストスイッチが値増加の途中で起こった場合には、こうなる:
見ての通り、結果がいっしょじゃないよな!
Foundation をリソースを共有するために使うスレッド間でリソースを共有するときの、一般的な解決方法は、ロックを使うことだ(ミューテックスとか、セマフォとか言われているやつね)。Foundation は、そのために NSLock 一族を提供しているんだ(NSLock、NSRecursiveLock、NSConditionLock、NSDistributedLock)。次のコードは、仮に作った NSMutableDictionary クラスだ。ロックを使って、スレッドセーフにしているんだ:
気をつけてほしいのは、ロックのための 2 つの呼び出しがあるんだけど、クリティカルセクションの中で例外が発生したら、それを捕まえないといけないんだ。もし捕まえなかったら大変なことが起こるぜ!ロックはロックしつづけてしまうから、次にこれが呼ばれたとき、デッドロックになるんだ(1)。あと、IMP を使えば、少し最適化できるよ(前の記事を見てね)。 これはそんなに遅く見えないんだけど、テストをやってみると遅い。私がやったときは、スレッドセーフのコードは 9.2 秒で、普通のコードは 5.2 秒だった。(NS_DURING を使わないと 7.0 秒で、IMP を使って NS_DURING を使わないと 6.8 秒だったよ) スレッドセーフを必要として、何度も呼ばれるようなルーチンは、パフォーマンスの重荷になることが証明されたね!
(1) この場合、object が nil の時だけ、例外が発生するんだ。だから、自分で nil チェックをすれば、例外を回避することができて、NS_DURING のコードは消せるんだ。 |
|
Home | Link | Download | Back Number | Speciall Issue
|