Boost的rtree可以从并行线程进行查询吗?

3
我有两个应用程序模块。Module1 拥有并构建 boost::geometry::index::rtree。Module2 向 Module1 提出查询,这些查询被传递到 RTree。现在我想加快速度,并拥有多个 Module2 实例,这些实例向一个 Module1 实例提出查询,并单独工作。我非常确定,在任何 Module2 工作时,RTree 都不会改变。
我找到了这个问题: Can I use Boost.Geometry.index.rtree with threads?,但它描述了更复杂的情况,即从不同线程修改和查询 rtree。这个答案是含糊的:“没有任何方式可以保证 boost Rtree 是线程安全的”。但在评论中,它被声明为“进行查询是安全的,甚至可以创建解决方法”。什么是正确的答案?除了直接向 boost 作者提问之外,还有其他资源可以找到答案吗?
Tl;dr:

如果我可以百分之百确定没有线程修改RTree,那么从不同的线程查询boost::geometry::index::rtree是安全的吗?


1
你的问题的答案似乎包含在你链接的问题的答案中(就在底部)。 - Rostislav
可能是Can I use Boost.Geometry.index.rtree with threads?的重复问题。 - erasmortg
@Rostislav 在回答链接问题时说:“没有任何方式可以使boost Rtree线程安全”。但在评论中:“查询是安全的,甚至可以创建解决方案来进行创建”。谁是正确的? - DoctorMoisha
2个回答

3
回答相关问题:"No boost Rtree is not thread-safe in any way"。但在评论中:"It is safe to do queries, and it even possible to create workaround for creation"。谁是对的?
没有矛盾。Adam是作者,每个人都是正确的。请注意,答案还说:
“你/可以/并行运行多个只读操作。通常情况下,库容器用于多线程的只读操作是安全的(尽管您可能会想要快速扫描任何隐藏的可变成员(在实现中))。”
一般来说,只要位表示不发生变化,就可以安全地进行并发访问。这与库支持无关。
请注意,由于Adam Wulkiewicz的权威评论,您不需要进行“快速扫描”。
注释:这仍然不能使库线程安全。这只是因为C++的内存模型不会出现数据竞争和位常量数据。

通常情况下,库容器是安全的。但我真的不喜欢那个“通常”。实际上,SomeContainer::get 可以重新排列容器,或者它可以使用一些内部类变量并读/写其状态。例如,ISO CPP 11 指出,std::vector 的并发读取必须是线程安全的,所以好吧,我可以依赖它。关于 boost RTree,我可以依靠哪些信息? - DoctorMoisha
你想让我重复一遍吗?如果这还不够好,你应该在开发者列表中提交一个请求。关于“通常” - 我准确地说明了适用的条件以及为什么你可以依靠它来进行rtree。 - sehe
1
抱歉,我重新阅读了答案并决定谷歌一下Adam Wulkiewicz,他似乎是boost::geometry的贡献者。所以这是我期望的保证。 - DoctorMoisha

0

这似乎不是完整的问题。我读到的是两部分。第一部分应该是“我想优化我的程序。我该如何做?”

你应该使用性能分析器在优化之前进行测量!在此过程中,您可能会注意到有更重要的优化可用,如果您过早地引入多线程,则可能会将其推出可能性窗口。

你应该使用性能分析器在优化之后进行测量!发现优化无关紧要并不罕见。在多线程优化方面,从您的测量结果中,您应该看到处理一个任务需要稍长时间,但您可以在具有四核CPU的计算机上同时处理四到八个任务。如果稍长时间相当于4-8倍的因素,那么显然多线程是引入膨胀而不是优化的不必要介绍。

第二部分,您已经提供了这两个语句:

我100%确定,只要Module2工作,RTree就不会改变。
如果我100%确定没有线程修改RTree,那么从不同的线程对boost::geometry::index::rtree进行查询是否安全?
你应该使用锁。如果不使用锁,将会调用未定义的行为。稍后我会解释为什么应该使用锁。
我建议您使用读写锁(例如pthread_rwlock_t)来描述您所描述的用例。这将允许您的线程同时访问资源,只要没有线程尝试写入,并提供更新被推送到线程的栅栏。
为什么应该使用锁?首先,它们保证您的代码将正确运行;任何关于是否安全的担忧都将无效。其次,锁提供了一个栅栏,可以将更新推送到线程;与此相比,任何关于性能影响的担忧应该是微不足道的。

每个线程应该执行多个任务!这就是为什么需要使用栅栏的原因。如果您的线程最终终止并且您稍后创建了新线程,则会产生开销,这当然在执行优化时是不可取的。如果一个线程尽管预计后面还有更多这些任务,但终止了,则该线程可能应该被暂停。

期望您的优化可能会变成工作窃取线程池。这就是优化的本质,当我们针对最重要的优化时。毕竟,有时它远远是最重要的或者唯一的瓶颈。优化这样的瓶颈可能需要采取极端措施。

我之前强调过“应该可以忽略不计”,因为您只有在一定程度上才能看到性能的显着提高;试图在具有4个核心(每个核心2500个线程)的处理器上启动10000个线程(每个线程占用0.5至4.0MB堆栈空间,总共为5-40GB)显然不会非常优化。尽管如此,这是许多人犯错的地方,如果他们有一个分析器来指导他们,他们将更有可能注意到......

如果您的任务涉及可以变为非阻塞的IO,则甚至可以在一个线程上运行多个任务。通常,在查看多线程之前,我会研究这种优化,因为分析器会突出显示。


2
我很感激你的好意,但我的问题非常明确。我之所以这样问,是因为我知道我想要哪种优化,而且我不想使用任何锁。 - DoctorMoisha
@DoctorMoisha 如果您每个线程只执行一个任务(然后线程终止),那么您将错过此优化的潜在好处。多线程引入的瓶颈的很大一部分是在启动线程时。如果您正在为执行一个任务而旋转线程,让线程终止,然后再旋转更多线程,那么与挂起和同步完成每个作业后恢复的线程池相比,您将失去效率。我已经尝试在我的答案中阐述这一点,但我现在不在最佳状态。 - autistic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接