0

该算法在本文中描述:多道程序多处理器的线程调度。简而言之,计算分布在进程中,每个进程都有一个线程双端队列来完成这项工作。进程可以将(弹出)线程推送到(从)其双端队列的底部,而其他进程可以通过从顶部弹出线程来窃取它的工作。因此,可以通过推送操作动态地创建工作。算法如下。

调度算法

我的问题是关于 popTop() 工作窃取功能。我不认为它适用于所有情况。例如,假设一个拥有队列 Q 的进程 A 和一个试图从 Q 窃取工作的进程 B,调用 popTop()。假设此时 B 在 popTop() 的第 2 行和 localBot = X 之后被抢占。如果 A 运行并且 popBottom() 直到 Q <= X 的底部,当 B 恢复运行时,它将获得一个已经被 A 处理的线程。

我的想法正确吗?我需要验证它,因为我将实现它以在 CUDA 程序中进行工作平衡。

4

1 回答 1

2

该代码使用 cas() (比较和交换)来尝试停止您所描述的那种事情。如果 popTop() 在第 2 行之后停止,它会在读取 age 和 bot 后停止。如果 popBottom() 然后运行并返回一个线程,它将在 age 内增加字段并使用 cas() 将增加的版本写回内存。现在当 B 恢复并调用 cas() 时, cas() 指令发现 B 提供的年龄值与内存中的值不匹配(这意味着对 cas() 的调用不会修改内存)。所以 B 找到 (oldAge == newAge) 并返回 ABORT。在这种情况下,您通常会再试一次,并希望下次有更好的运气。这篇文章似乎是说,调用 yield() 对你有不错的运气是必要的,但无论如何 popTop() 都不应该返回其他人已经抓住的线程。

当然,在http://en.wikipedia.org/wiki/Compare-and-swap上有一篇关于 cas() 的维基百科文章。

我会将使用锁的并行代码置于串行代码之上一级难度,将无锁并行代码置于锁定并行代码之上一级难度。我不会编写无锁并行代码,除非我确定我需要性能,并且没有可以重用的已知可信代码。在我彻底测试之前,我不会相信这样的代码,如果可能的话,我实际上更愿意进行模型检查。

于 2012-07-04T17:55:54.007 回答