我一直在修补BSP树一段时间,我也在玩线程。向BSP树添加三角形时,有机会创建一个新线程,以便并行处理数据。

insert(triangle, bspnode)
{
  ....
  else if(triangle spans bspnode)
  {
    (frontpiece, backpiece) = plane_split(triangle, bspnode)

    insert(frontpiece, bspnode.front)
    insert(backpiece, bspnode.back)
  }
  ....
}

上面的两个插入操作可以由两个线程执行,由于它们不会修改相同的数据,因此可以使用廉价的同步。

insert(triangle, bspnode)
{
  ....
  else if(triangle spans bspnode)
  {
    (frontpiece, backpiece) = split(triangle, bspnode)

    handle = beginthread(insert(backpiece, bspnode.front))
    insert(frontpiece, bspnode.back)
    if(handle)
    {
      waitforthread(handle)
    }
    else
    {
      insert(backpiece, bspnode.front)
    }
  }
  ....
}

这个新方法尝试创建一个并行完成操作的线程,但是如果无法创建线程则不应该失败(它只会恢复到原始算法)。

这是一种合理的编程习惯,还是我使用的线程不正确?我还没有找到关于这种技术的任何文献。我喜欢它倾向于使用我的CPU(2核),理论上可以扩展到任何数量的可用处理器。我不喜欢它在CPU和内存上可能非常浪费。

有帮助吗?

解决方案

如果处理的某些部分正在等待外部事件(用户输入,I / O,其他一些处理),则线程很好 - 正在等待的线程可以继续等待,而一个没有等待前进的线程

但是,对于处理密集型任务,比处理器更多的线程实际上会产生开销。看起来你的线程正在做所有的“CPU工作”,所以我坚持每个核心一个线程 - 测试以找到最佳数量。

创建的最大开销来自上下文切换(冻结一个线程并加载下一个线程的执行上下文),以及线程执行具有不同内存的任务时的缓存未命中(如果您的线程可以有效地使用CPU缓存)

其他提示

最好的办法是创建一个线程池,然后“透明地”使用它来添加节点。

例如,在程序启动时创建2个线程,让它们等待信号量或事件。当您要添加节点时,将数据弹出到队列中然后触发信号量。这会唤醒其中一个线程,它将数据从队列中弹出并执行处理。 (确保对队列的访问是线程安全的 - 最好与关键部分完全同步)。

你的应用程序的整体性能较慢,因为你有更多的开销,将数据复制到队列和运行额外的线程,但如果你曾经在单核上运行,你现在将运行2.它最好的如果线程处理很昂贵。

当然,例如,Quicksort可以很容易地编程多线程,并在多核系统上获得一些大的性能提升,并且在非多线程系统上有一些小的性能损失。请记住,你现在要增加两次开销 - 一次是为了保存递归,一次是在线程上,所以如果你进行了大量的递归,那么它可能比非多线程方法更快地压倒系统。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top