【问题标题】:Can queries to boost's rtree be done from parallel threads?可以从并行线程完成对 boost rtree 的查询吗?
【发布时间】:2015-09-23 12:08:16
【问题描述】:

我在应用程序中有两个模块。 Module1 拥有并构建 boost::geometry::index::rtreeModule2Module1 进行查询,这些查询被传递给 RTree。现在我想加快速度并拥有几个 Module2 实例,它们对 一个 Module1 实例进行查询,并分别工作。我 100% 确定,虽然 任何 Module2 工作的 RTree 都不会改变

我发现了这个问题:Can I use Boost.Geometry.index.rtree with threads?,但它描述了更复杂的情况,当 rtree 被修改并从不同的线程查询时。这个答案是模棱两可的:答案中说明了“No boost Rtree is not thread-safe in any way”。但是在 cmets 中声明:“进行查询是安全的,甚至可以为创建创建解决方法”。什么是正确答案?除了直接提出问题以提升作者的能力外,是否有其他资源可以找出答案?

Tl;博士:

如果我 100% 确定没有线程修改 RTree,那么从不同线程对 boost::geometry::index::rtree 进行查询是否安全?

【问题讨论】:

  • 您问题的答案似乎包含在您链接的问题的答案中(就在底部)。
  • @Rostislav 回答链接的问题:“No boost Rtree 无论如何都不是线程安全的”。但在 cmets 中:“进行查询是安全的,甚至可以为创建创建解决方法”。谁是对的?

标签: c++ multithreading c++11 boost boost-geometry


【解决方案1】:

在回答链接问题时:“No boost Rtree 无论如何都不是线程安全的”。但在 cmets 中:“进行查询是安全的,甚至可以为创建创建解决方法”。谁是对的?

没有矛盾。亚当是作者。每个人都是对的。注意答案还说

您/可以/并行运行多个只读操作。通常,库容器可以安全地从多个线程用于只读操作(尽管您可能希望快速扫描任何隐藏的可变成员(在实现中)。

一般来说,只要按位表示不发生变化,所有并发访问都是安全的。这与库支持无关。

请注意,由于 Adam Wulkiewicz 的权威评论,您不需要“快速扫描”。


脚注:这仍然不能保证库线程安全。这完全正确,因为 C++ 的内存模型不存在与按位常量数据竞争的情况。

【讨论】:

  • 通常,库容器是安全的 我真的不喜欢这样。 SomeContainer::get 实际上可以重新排列容器,或者它可以使用一些内部类变量并读/写它的状态。例如 ISO CPP 11 规定,std::vector 并发读取必须是线程安全的,所以好吧,我可以依赖它。关于 boost RTree,我可以依赖哪些信息?
  • 您要我重复我说的话吗?如果这还不够好,您可能应该在开发人员列表中提出请求。 (回复:“通常”-我限定了精确地适用哪些条件以及为什么您可以依赖它来实现 rtree。)
  • 抱歉,我重读了答案并决定用谷歌搜索 Adam Wulkiewicz,他似乎是 boost::geometry 的贡献者。所以这是我所期望的保证。
【解决方案2】:

这似乎不是完整的问题。我正在阅读的内容分为两部分。第一部分应该是“我想优化我的程序。我应该怎么做?”

您应该在优化之前使用分析器进行测量! 在此过程中您可能会注意到有更多重要的优化可供您使用,如果出现这些优化可能会被排除在外你过早地引入了多线程。

您应该在优化后使用分析器进行测量!发现优化微不足道的情况并不少见。在多线程优化方面,根据您的测量,您应该看到处理一个任务需要稍长,但您可以一次处理四个到八个 在具有四核 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,您甚至可以在一个线程上运行多个任务。这通常是我在研究多线程之前会研究的优化,因为分析器会突出显示。

【讨论】:

  • 感谢您的好意,但我的问题很准确。我问这个,因为我知道我想要哪种优化,而且我不想使用任何锁。
  • @DoctorMoisha 如果每个线程只执行一项任务(然后线程终止),那么您将错过此优化的潜在好处。多线程引入的大部分瓶颈是在启动线程时。如果您为了执行一项任务而启动线程,让线程终止然后启动更多线程,那么与在完成每个作业后恢复时暂停和同步的线程池相比,您将失去效率。我试图在我的回答中详细说明这一点,但我不是在最好的空间。
猜你喜欢
  • 1970-01-01
  • 2016-06-06
  • 2018-04-07
  • 1970-01-01
  • 1970-01-01
  • 2021-09-21
  • 2023-03-21
  • 1970-01-01
相关资源
最近更新 更多