Я реализовал алгоритм A* в Go, чтобы найти путь между двумя координатами на карте. Данные карты извлекаются с помощью mgo из коллекции MongoDB.
Однако это очень медленно. Он составляет около 4 секунд для маршрута на 1000 метров. Я рассчитал время для различных частей алгоритма и пришел к выводу, что узкое место находится в выборке из базы данных. Или, если быть точным: в преобразовании бинарных данных в структуру данных, которую понимает Go.
Я стараюсь делать как можно меньше запросов и многопотоковые запросы, чтобы сделать это быстрее, и это в определенной степени помогает. Но это не так быстро, как хотелось бы.
Кажется, я делаю что-то принципиально неправильное. Любые предложения вообще будут полезны.
Структура данных в mongoDB: (узлы получены из OSM)
{ "_id" : NumberLong(194637483),
"lat" : 55.7079899,
"lon" : 13.3756414,
"neighbours" : [ NumberLong(1566264689), NumberLong(1566264806) ]
}
Структура данных в Go
type Node struct {
ID int64 `bson:"_id" json:"id"`
Lat float64 `bson:"lat" json:"lat"`
Lon float64 `bson:"lon" json:"lon"`
Neighbours []int64 `bson:"neighbours" json:"neighbours"`
}
Код для получения части данных:
func addToBufferLong(buffer *WriteLockMap, session *mgo.Session, at, goal geo.Node, lowerLat, higherLat, lowerLon, higherLon float64) {
c := session.DB("collectionName").C("nodes")
query := c.Find(bson.M{"lat": bson.M{"$gt": lowerLat, "$lt": higherLat}, "lon": bson.M{"$gt": lowerLon, "$lt": higherLon}})
var nodes []geo.Node
query.All(&nodes)
for _, node := range nodes {
tmp := PathNode{0, node.DistanceTo(goal), 0, node}
buffer.Lock()
buffer.m[node.ID] = tmp
buffer.Unlock()
}
}
Редактировать:
Стратегия многопоточности основана на разделении области, которую я хочу запросить, на 4 разных квадрата, квадранта, если хотите, и выполнении их отдельно с помощью addToBufferLong(...)
Последние распечатки:
> time GOMAXPROCS=8 ./toystar
Starting A star
Thread A, count: 19657, db-fetch: 0.122792104s, parsing: 0.934650055s
Thread B, count: 19574, db-fetch: 0.274384302s, parsing: 1.196350664s
Thread C, count: 4197, db-fetch: 0.388197823s, parsing: 0.930109241s
Thread D, count: 9900, db-fetch: 0.540008325s, parsing: 0.93963728s
Total database fetch: 1.534268099 s
Total A star (with fetches): 1.854748244
real 0m1.907s
где db-fetch измеряет время, необходимое для выполнения строки, которая начинается с query := c.Find(...), а синтаксический анализ измеряет время, необходимое для выполнения query.All(&nodes)
Редактировать 2:
Мне удалось значительно сократить время выполнения с помощью других пользователей переполнения стека. Текущие распечатки:
> time GOMAXPROCS=8 ./toystar
Starting A star
Thread A: 0.210783141s
Thread B: 0.380938949s
Thread C: 0.441447793s
Thread D: 0.507361847s
Total database fetch: 0.507543875 s
number of fetches: 1
Total A star: 0.826343287s
real 0m0.860s
Основное отличие заключается в многопоточной стратегии и использовании *mgo.Iter
вместо query.All(&nodes)
.
(geo.Node).DistanceTo(geo.Node)
. Кроме того, какова ваша многопоточная стратегия, то есть как вы распределяете работу между рабочими? - person thwd   schedule 15.10.2015runtime/pprof
(или альтернативный вариант ). - person thwd   schedule 15.10.2015