9

覚えているかもしれませんが、私はGCDを使用して、コードの一部、つまり衝突検出および解決エンジンを高速化しようとしています。ただし、すべてのGCDコードがシリアルコードよりも大幅に遅く、一貫性が低いため(1.4倍から10倍遅い)、明らかに間違ったことをしています。例を挙げましょう。バブルソート方式で配列を反復処理して、その配列内のオブジェクト間で発生する可能性のあるすべての衝突を特定します。

- (double) detectCollisionsInArray:(NSArray*)objects
{   
    int count = [objects count];
    if (count > 0)
    {       
        double time = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < count; i++)
        {
            for (int j = i + 1; j < count; j++)
            {
                /** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
            }
        }

        return CFAbsoluteTimeGetCurrent() - time;
    }

    return 0;
}

非常に簡単で、問題の制約を考えるとうまく機能しているようです。ただし、コードセクションで各オブジェクトの状態が変更されていないことを利用し、GCDを使用してこの作業を並列化したいと思います。これを行うために、私は次のようなことを試みています:

- (double) detectCollisionsInArray:(NSArray*)objects
{   
    int count = [objects count];
    if (count > 0)
    {
        NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
        NSBlockOperation* blockOperation = nil;

        double time = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < count; i++)
        {
            for (int j = i + 1; j < count; j++)
            {
                void (^workBlock) (void) = ^() 
                {
                    /** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
                };

                if (!blockOperation)
                {
                    blockOperation = [NSBlockOperation blockOperationWithBlock:b];
                }
                else
                {
                    [blockOperation addExecutionBlock:workBlock];
                }
            }
        }

        [opQueue addOperation:blockOperation];
        [opQueue autorelease];

        return CFAbsoluteTimeGetCurrent() - time;
    }

    return 0;
}

誰かが私を正しい軌道に乗せるのを手伝ってくれて、おそらく良いGCDチュートリアルへのリンクを提供できますか?私はいくつかのGCDチュートリアルを調べ、すべてのドキュメントを精査しましたが、それでもこの主題についての私の理解はせいぜい希薄であると感じています。ありがとう!

4

2 に答える 2

30

dispatch_*GCD C APIと関数ファミリーを使用していない理由はありますか?NSOperationQueueのGCDの側面(ブロックを送信するキューなど)をあまり制御できません。また、iOSを使用しているかどうかはわかりませんが、iOSではGCDを使用NSOperationQueueしていません。それが非常に多くのスレッドを生成した理由かもしれません。いずれにせよ、GCD APIを直接使用すると、コードは短く簡単になります。

- (double) detectCollisionsInArray:(NSArray*)objects
{   
  int count = [objects count];
  if (count > 0)
  {
    double time = CFAbsoluteTimeGetCurrent();

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    for (int i = 0; i < count; i++)
    {
      dispatch_group_async(group, queue, ^{
        for (int j = i + 1; j < count; j++)
        {
          dispatch_group_async(group, queue, ^{
            /** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
          });
        }
      });
    }
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_release(group);
    return CFAbsoluteTimeGetCurrent() - time;
  }
  return 0;
}

を使用して、dispatch_groupすべての実行をグループ化し、すべてがで終了するのを待つことができますdispatch_group_wait。ブロックがいつ終了するかを気にしない場合は、グループ部分を無視して、を使用できますdispatch_async。このdispatch_get_global_queue関数は、ブロックを送信するための3つの同時キュー(低、デフォルト、または高優先度)のいずれかを取得します。スレッド数の制限などについて心配する必要はありません。GCDスケジューラーはあなたのためにそれらすべてを行うことになっています。並行キューに送信するようにしてください。同時キューは、3つのグローバルキューのいずれか、またはに渡すDISPATCH_QUEUE_CONCURRENTことで作成したキューのいずれかですdispatch_queue_create(これは、OS X10.7およびiOS5.0以降で使用できます)。

各ブロックでファイルI/Oを実行している場合、または他のリソースに負担をかけている場合は、GCDを管理し、一度にキューに送信するブロックの数を制限する必要があります。これは、の同時操作数を制限するのと同じ効果がありNSOperationQueueます。これを行うには、GCDセマフォを使用できます。

- (double) detectCollisionsInArray:(NSArray*)objects
{   
  int count = [objects count];
  if (count > 0)
  {
    double time = CFAbsoluteTimeGetCurrent();

    dispatch_group_t group = dispatch_group_create();
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    for (int i = 0; i < count; i++)
    {
      dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
      dispatch_group_async(group, queue, ^{
        for (int j = i + 1; j < count; j++)
        {
          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
          dispatch_group_async(group, queue, ^{
            /** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
            dispatch_semaphore_signal(semaphore);
          });
        }
        dispatch_semaphore_signal(semaphore);
      });
    }
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_release(group);
    dispatch_release(semaphore);
    return CFAbsoluteTimeGetCurrent() - time;
  }
  return 0;
}

一度コツをつかめば、GCDの使い方はとても簡単です。今はコード全体で使用しています。

誰かが私を正しい軌道に乗せるのを手伝ってくれて、おそらく良いGCDチュートリアルへのリンクを提供できますか?

実行します。MikeAshのブログにアクセスしないでください。彼のGCDに関するシリーズは、私が見た中で最も明確で簡潔なものであり、すべてを読むのに約30分しかかかりません。GCDとブロックに関する2010年のAppleのWWDCビデオもかなり良いです。

于 2011-02-22T20:29:58.690 に答える
4

forコードでは、ネストされたループが終了するまで、各オブジェクトに対して実行する必要のある作業を遅らせています。とは言うものの、ループが終了すると、多数のオブジェクトに対して多数のブロックを使用する1つの操作が行われるため、GCDを適切に利用できなくなります。

NSBlockOperationオブジェクトごとに1つ作成し、各反復NSOperationQueueの最後に追加することをお勧めします。for (int j = i + 1; j < count; j++)

このように、システムは、反復が終了するとすぐに、各オブジェクトに対して実行する必要のある作業の処理を開始します。

また、キューは使用可能なプロセッサよりも大きくならないようにする必要があります。そうしないと、スレッド切り替えプロセスにオーバーヘッドが発生し、速度が低下します。

于 2011-02-20T21:16:05.927 に答える