9 Stimmen

GCD Schlechte Leistung

Wie Sie sich vielleicht erinnern, versuche ich, GCD zu verwenden, um einen Teil meines Codes zu beschleunigen, nämlich eine Kollisionserkennung und eine Auflösungsmaschine. Allerdings mache ich offensichtlich etwas falsch, denn mein gesamter GCD-Code ist deutlich langsamer und weniger konsistent als mein serieller Code (zwischen 1,4x und 10x langsamer). Erlauben Sie mir, Ihnen ein Beispiel zu geben: Ich iteriere über ein Array in einer Bubble-Sortierweise, um alle möglichen Kollisionen zwischen den Objekten in diesem Array zu ermitteln:

- (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;
}

Ziemlich einfach, und es scheint angesichts der Einschränkungen des Problems gut zu funktionieren. Ich möchte jedoch die Tatsache ausnutzen, dass der Zustand der einzelnen Objekte im Codeabschnitt nicht geändert wird, und GCD verwenden, um diese Arbeit zu parallelisieren. Um dies zu erreichen, versuche ich etwas in der Art:

- (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;
}

Kann mir jemand helfen, mich auf den richtigen Weg zu bringen und vielleicht einen Link zu einem guten GCD-Tutorial zur Verfügung stellen? Ich habe mir mehrere GCD-Tutorials angesehen und die gesamte Dokumentation durchforstet, und ich habe immer noch das Gefühl, dass mein Verständnis für das Thema bestenfalls dürftig ist. Vielen Dank!

30voto

Nathan Eror Punkte 12388

Gibt es einen Grund, warum Sie nicht die GCD C API und das dispatch_* Familie von Funktionen? Sie haben keine große Kontrolle über die GCD-Aspekte von NSOperationQueue (z. B. in welche Warteschlange Sie die Blöcke stellen wollen). Außerdem kann ich nicht sagen, ob Sie iOS verwenden oder nicht, aber NSOperationQueue tut no GCD auf iOS verwenden. Das könnte der Grund dafür sein, dass so viele Threads entstanden sind. So oder so, Ihr Code wird kürzer und einfacher sein, wenn Sie die GCD-API direkt verwenden:

- (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;
}

Sie können eine dispatch_group alle Ausführungen zusammenzufassen und zu warten, bis sie alle mit dispatch_group_wait . Wenn es Sie nicht interessiert, wann die Blöcke fertig sind, können Sie den Gruppenteil ignorieren und einfach dispatch_async . El dispatch_get_global_queue erhält eine der 3 gleichzeitigen Warteschlangen (niedrige, Standard- oder hohe Priorität), an die Sie Ihre Blöcke senden können. Sie sollten sich keine Gedanken über die Begrenzung der Threadanzahl oder ähnliches machen müssen. Der GCD-Scheduler sollte all das für Sie erledigen. Stellen Sie einfach sicher, dass Sie Ihre Blöcke an eine gleichzeitige Warteschlange übergeben, die entweder eine der 3 globalen Warteschlangen oder eine von Ihnen erstellte Warteschlange sein kann, indem Sie DISPATCH_QUEUE_CONCURRENT a dispatch_queue_create (dies ist ab OS X 10.7 und iOS 5.0 verfügbar).

Wenn Sie in jedem Block Dateieingaben vornehmen oder eine andere Ressource beanspruchen, müssen Sie möglicherweise GCD zügeln und die Anzahl der Blöcke begrenzen, die Sie gleichzeitig an die Warteschlange senden. Dies hat den gleichen Effekt wie die Begrenzung der Anzahl gleichzeitiger Operationen in einer NSOperationQueue . Sie können dazu ein GCD-Semaphor verwenden:

- (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;
}

Sobald Sie den Dreh raus haben, ist GCD sehr einfach zu bedienen. Ich verwende es jetzt überall in meinem Code.

Kann mir jemand helfen, mich auf den richtigen Weg zu bringen und vielleicht einen Link zu einem guten GCD-Tutorial zur Verfügung stellen?

ausführen. gehen Sie nicht hinüber zu Mike Ashs Blog . Seine Serie über GCD ist die klarste und prägnanteste, die ich je gesehen habe, und Sie werden nur etwa 30 Minuten brauchen, um das Ganze zu lesen. Die WWDC-Videos von Apple aus dem Jahr 2010 über GCD und Blöcke sind ebenfalls sehr gut.

4voto

Tiago Punkte 1327

In Ihrem Code verzögern Sie die Arbeit, die Sie für jedes Objekt tun müssen, bis zum Ende der verschachtelten for Schleife. Das heißt, wenn die Schleife beendet ist, haben Sie eine Operation mit vielen Blöcken für eine Reihe von Objekten, und Sie werden dadurch die Vorteile des GCD nicht richtig nutzen.

Ich würde vorschlagen, dass Sie einen erstellen NSBlockOperation für jedes Objekt und fügen Sie es der NSOperationQueue am Ende eines jeden for (int j = i + 1; j < count; j++) Iteration.

Auf diese Weise beginnt das System mit der Verarbeitung der Arbeit, die Sie für jedes Objekt erledigen müssen, sobald die Iteration beendet ist.

Denken Sie auch daran, dass die Warteschlange nicht viel größer sein sollte als die verfügbaren Prozessoren, da sonst beim Threadwechsel ein gewisser Overhead entsteht, der die Geschwindigkeit beeinträchtigt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X