我在Go中实现了一个A *算法来查找地图上两个坐标之间的路径。使用MongoDB集合中的mgo获取地图数据。
然而,它很慢。 1000米路线大约需要4秒钟。我已经计算了算法的不同部分,并得出结论,瓶颈是从数据库中取出的。或者确切地说:在从二进制数据到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(...)开头的行所花费的时间,并解析测量查询所需的时间。所有(&节点)
在堆栈溢出用户的帮助下,我成功地显着降低了执行时间。目前的打印输出:
> 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)
答案 0 :(得分:2)
根据现有资料,我可以推断出这一点:
您调用db-fetch
的阶段实际上并不访问数据库。 c.Find(...)
唯一能做的就是构建*mgo.Query
值。该方法长6行。这不应该超过一毫秒。除非在数据库对象的内部会话状态上存在争用,但由于您只使用了4个goroutine,因此情况似乎并非如此。
query.All(&nodes)
是在数据库上实际执行查询的地方。此外,此阶段分配所需的节点片段,然后通过反射迭代地将bson解码为结构定义。
*mgo.iter
您可以使用query.All(...)
获取query.Iter()
而不是*mgo.Iter
,而不是批量遍历数据集。这可以通过更好地分配网络负载来提高性能。
见the documentation。也许你已经这样做了。如果没有,它可能会改善查找时间。
我认为这一点很明显。分而治之,对吧? :)