рекурсивный параллелизм для quadtree с golang - PullRequest
0 голосов
/ 02 июля 2018

Я пытаюсь распараллелить свой поиск по дереву, используя подпрограммы go, чтобы разделить рекурсивный поиск.

Моя структура Quadtree выглядит следующим образом:

type Quadtree struct {
    Rectangle //Boundary of the quadtree
    Points   []Point
    Ne       *Quadtree
    Se       *Quadtree
    Sw       *Quadtree
    Nw       *Quadtree
    Divided  bool
    Capacity int
    Parent   *Quadtree
}

У меня есть метод с именем QueryForNearestPoints, который принимает Point и searchRadius и возвращает все точки в пределах searcharea в Quadtree. Если в данном searchRadius точек не найдено, то значение searchRadius увеличивается и пробуется снова.

points - канал, на который отправляются точки в радиусе поиска. Я также использую sync.WaitGroup wg, чтобы дождаться завершения всех процедур.

func (q *Quadtree) QueryForNearestPoints(p Point, searchRadius float64) []QueryResult {
    searcharea = Circle{p, searchRadius}
    var testResults []QueryResult

    for {

        go func() {
        loop:
            for {
                select {
                case item, _ := <-points:
                    testResults = append(testResults, item)
                case <-done:
                    break loop
                }
            }
        }()
        wg.Add(1)
        go q.query(searcharea)
        wg.Wait()
        done <- true
        if len(testResults) > 0 {
            break
        } else {
            // Proportionally increase the search radius if no points are found.
            searcharea = Circle{p, searcharea.Radius + searcharea.Radius*50/100}
        }
    }
    return testResults
}

Метод запроса распараллеленного квадратного дерева:

func (q *Quadtree) query(area Circle) {
    defer wg.Done()
    if !q.overlapsCircle(area) {
        return
    }
    if len(q.Points) > 0 {
        for i, point := range q.Points {
            if area.contains(point) {
                points <- QueryResult{point, i, q}
            }
        }
    }
    if q.Divided {
        wg.Add(4)
        go q.Ne.parallelquery(area)
        go q.Se.parallelquery(area)
        go q.Sw.parallelquery(area)
        go q.Nw.parallelquery(area)
    }
}

(q *Quadtree) parallelquery метод:

func (q *Quadtree) parallelquery(area Circle) {
    semaphore <- struct{}{}
    defer func() {
        <-semaphore
    }()
    q.query(area)
}

Если я не ошибаюсь, это рабочая нагрузка, связанная с процессором, так что выполнение тысяч подпрограмм не поможет, поэтому я использую буферизованный канал semaphore в качестве счетного семафора, чтобы убедиться, что при в любой точке активны только n количество подпрограмм (здесь я сделал n как 4, потому что у моего компьютера 4 процессора).

Единственная проблема заключается в том, что этот подход значительно медленнее, чем обычный не параллельный последовательный подход. Что мне здесь не хватает? Как я могу сделать это быстрее?

1 Ответ

0 голосов
/ 03 июля 2018

Это намного медленнее, потому что работа, которую вы выполняете в каждой из программ, очень дешевая, и там очень много программ.

Для тех, кто будет критиковать терминологию в этом ответе, я буду использовать параллелизм и параллелизм взаимозаменяемо. Даже если строго, параллелизм - это свойство среды выполнения / компьютера, а параллелизм - это свойство кода. Некоторый код все еще может быть параллельным, если он выполняется на оборудовании с одним процессором, но он не будет работать параллельно.

Причина, по которой параллельная версия здесь медленнее, заключается в том, что при использовании каналов и т. Д. Все еще требуется некоторая синхронизация, поэтому каналы используют мьютекс для синхронизации отправки / получения на канале. Это означает, что вы вводите раздор между goroutines.

Каждый раз, когда вы звоните parallelquery(area), вы запускаете еще 4 подпрограммы, которые должны быть запланированы, но (используя семафорный канал) вы ограничиваете количество одновременных подпрограмм до 4.

После n рекурсий у вас есть 4 ^ n программ, которые пытаются получить значение из канала семафора, это вызывает много споров.

Используя блокирующий профилировщик ЦП, вы, вероятно, обнаружите, что большая часть времени выполнения тратится на состязание при попытке получить токен из семафора.

...