我正在编写一本关于使用.NET 4进行多核编程的书,我很好奇人们在多核编程方面觉得难以理解或预计难以理解的部分是什么?
我正在编写一本关于使用.NET 4进行多核编程的书,我很好奇人们在多核编程方面觉得难以理解或预计难以理解的部分是什么?
什么是一个有用的工作单元可以并行化,如何找到/组织一个?
所有这些并行计算原语都是无用的,如果你分叉了一个小于分叉开销的工作,那么实际上会给你带来一个不错的减速而不是你期望的加速。
因此,其中一个大问题是找到显然比并行计算原语更昂贵的工作单元。这里的一个关键问题是,没有人知道任何执行的成本,包括并行计算原语本身。显然,校准这些成本将非常有帮助。(顺便说一下,我们设计、实现和日常使用一个并行编程语言PARLANSE,其目标是通过允许编译器生成和优化它们,使较小的工作单元“更容易并行化”来最小化并行计算原语的成本。)
我们也可以考虑讨论大O符号及其应用。我们都希望并行原语的成本为O(1)。如果是这种情况,那么如果您找到成本为O(x) > O(1)的工作,则该工作是并行化的好候选对象。如果您提出的工作也是O(1),则其有效性取决于常数因子,我们又回到了上面的校准问题。
如果没有足够大的单元收集工作,就会出现问题。代码移动、算法替换等都是实现此效果的有用方法。
最后,还有同步问题:我的并行单元何时必须交互,应该使用哪些原语,以及这些原语的成本如何?(比您预期的要高!)
由于您已经写了一本关于在.Net中进行多核编程的书,我认为您可以稍微超越多核。
例如,您可以使用一章节来讨论在.Net分布式系统中的并行计算。不过目前在.Net中还没有成熟的框架,DryadLinq是最接近的。(另一方面,Java平台中的Hadoop及其相关工具确实非常好用。)
您也可以使用一章节来演示一些GPU计算的内容。
有一件事让我感到困惑,那就是解决特定类型问题时应该采用哪种方法。有代理,有任务,异步计算,MPI 分布式计算等多种方法可供选择,但对于许多问题,我难以理解为什么应该优先选择其中的一种方法。
人们经常试图从多个线程更新数据结构,发现太难了,然后有人插话说“使用不可变的数据结构!”,于是我们的持久编码器写下了以下内容:
不可变数据结构到底是什么?
这并不是那么理论上的细节,而更多的是实际实现的细节使人们感到困惑。
ImmutableSet set;
ThreadLoop1()
foreach(Customer c in dataStore1)
set = set.Add(ProcessCustomer(c));
ThreadLoop2()
foreach(Customer c in dataStore2)
set = set.Add(ProcessCustomer(c));
Coder一直以来都听说过不可变数据结构可以在不加锁的情况下进行更新,但新代码出现了明显的问题。
即使你的目标对象是学者和经验丰富的开发人员,对于不可变编程习惯的基础知识的简要介绍也有好处。
如何在线程之间分配大致相等的工作量?
正确地完成这一步是很难的。有时,您需要将一个单一的进程分成 10,000 个可以并行执行的步骤,但并非所有步骤所需的时间都相同。如果您将工作分配给 4 个线程,并且前三个线程在 1 秒钟内完成,而最后一个线程需要 60 秒,那么您的多线程程序与单线程版本基本相同,对吗?
那么,如何在所有线程之间分配大致相等的工作量?解决垃圾箱装填问题的许多良好试探方法应该也适用于此。
多少线程?
如果您的问题可以很好地并行化,则添加更多线程应该会使其更快,对吗?嗯,并非完全如此,在这里需要考虑很多东西:
即使是单核处理器,添加更多线程也可以使程序运行得更快,因为更多的线程提供了更多机会让操作系统调度您的线程,从而使它比单线程程序获得更多的执行时间。但是,随着收益递减的法则,添加更多线程会增加上下文切换的数量,因此,在某个点上,即使您的程序具有最长的执行时间,其性能仍可能比单线程版本差。
那么,如何启动足够多的线程才能使执行时间最小化?
如果有很多其他应用程序启动线程并竞争资源,那么如何检测性能变化并自动调整您的程序呢?
我觉得在复杂的模式下,同步数据在工作节点之间移动的概念非常难以可视化和编程。
通常我发现调试也很麻烦。