GCD słaba wydajność

Jak zapewne pamiętacie, próbuję użyć GCD, aby przyspieszyć część mojego kodu, a mianowicie silnik wykrywania kolizji i rozdzielczości. Jednak wyraźnie robię coś złego, ponieważ cały mój kod GCD jest znacznie wolniejszy i mniej spójny niż mój kod seryjny (od 1,4 x do 10x wolniejszy). Pozwól, że podam ci przykład: iteruję nad tablicą w sposób bąbelkowy, aby określić wszystkie możliwe kolizje między obiektami w tej tablicy:

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

Pretty proste, i wydaje się działać dobrze biorąc pod uwagę ograniczenia problemu. Chciałbym jednak skorzystać z faktu, że stan każdego obiektu nie jest modyfikowany w sekcji kod i użyć GCD do równoległej pracy. Aby to zrobić staram się coś takiego:

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

Czy ktoś może mi pomóc umieścić mnie na właściwej ścieżce i może podać link do dobrego tutoriala GCD? Przejrzałem kilka samouczków GCD i przejrzałem całą dokumentację i nadal czuję że moja wiedza na ten temat jest co najwyżej słaba. Dzięki!

Author: Brad Larson, 2011-02-20

2 answers

Czy Jest jakiś powód, dla którego nie używasz API GCD C i rodziny funkcji dispatch_*? Nie masz zbyt dużej kontroli nad aspektami GCD NSOperationQueue (np. do której kolejki chcesz przesłać bloki). Ponadto, nie mogę powiedzieć, czy używasz iOS, czy nie, ale NSOperationQueue Czy nie używa GCD na iOS. To może być powód, dla którego zrodził tak wiele wątków. Tak czy Inaczej, twój kod będzie krótszy i prostszy, jeśli użyjesz bezpośrednio API GCD:

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

Możesz użyć dispatch_group, aby zgrupować wszystkie egzekucje razem i czekać na ich wszystkich, aby zakończyć z dispatch_group_wait. Jeśli nie chcesz wiedzieć, kiedy skończą się bloki, możesz zignorować część grupy i po prostu użyć dispatch_async. Funkcja dispatch_get_global_queue otrzyma jedną z 3 równoległych kolejek (low, default lub high priority) do wysłania bloków. Nie powinieneś się martwić o ograniczenie liczby wątków lub coś w tym stylu. Harmonogram GCD powinien zrobić to wszystko za Ciebie. Tylko upewnij się, że przesyłasz do kolejki równoległej, która może bądź jedną z 3 kolejek globalnych lub kolejką utworzoną przez przejście DISPATCH_QUEUE_CONCURRENT do dispatch_queue_create (jest ona dostępna od wersji OS X 10.7 i iOS 5.0).

Jeśli robisz jakieś operacje wejścia/wyjścia plików w każdym bloku lub opodatkowujesz inne zasoby, być może będziesz musiał zapanować w GCD i ograniczyć liczbę bloków, które jednocześnie przesyłasz do kolejki. Będzie to miało taki sam efekt jak ograniczenie liczby operacji współbieżnych w NSOperationQueue. W tym celu można użyć semafora 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;
}

Gdy dostaniesz hang of it, GCD jest bardzo prosty w użyciu. Używam go teraz w całym kodzie.

Czy ktoś może mi pomóc umieścić mnie na właściwej ścieżce i może podać link do dobrego tutoriala GCD?

Biegnij , nie podchodź do bloga Mike ' a Asha . Jego seria na GCD jest najjaśniejsza i najbardziej zwięzła, jaką widziałem, a przeczytanie całości zajmie ci tylko około 30 minut. Filmiki WWDC firmy Apple z 2010 roku na GCD i blockach też są całkiem niezłe.

 30
Author: Nathan Eror,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-28 23:20:38

W kodzie opóźniasz pracę, którą musisz wykonać dla każdego obiektu do końca zagnieżdżonej pętli for. To powiedziawszy, po zakończeniu pętli będziesz miał jedną operację z dużą ilością bloków dla kilku obiektów i nie będziesz w ten sposób prawidłowo korzystać z GCD.

Sugerowałbym utworzenie jednego NSBlockOperation dla każdego obiektu i dodanie go do NSOperationQueue na końcu każdej iteracji for (int j = i + 1; j < count; j++).

W ten sposób system zacznie przetwarzać pracę, którą musisz wykonać dla każdego obiektu jak tylko iteracja się skończy.

Należy również pamiętać, że kolejka nie powinna być znacznie większa niż dostępne procesory, w przeciwnym razie będziesz miał trochę narzutu na proces przełączania wątków, który pogorszy prędkość.

 4
Author: Tiago,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-20 21:16:05