多核程序中的同步扩展
先进的同步方法可以提升多核软件的性能。
为现代多核处理器设计软件带来了一个难题。传统的软件设计中,线程操作共享数据,其可扩展性有限,因为对共享数据更新的同步会串行化线程并限制并行性。另一种分布式软件设计中,线程不共享可变数据,消除了同步并提供了更好的可扩展性。但是,分布式设计使得实现共享数据结构自然提供的功能(如动态负载均衡和强一致性保证)具有挑战性,并且并非适用于所有程序。然而,通常情况下,共享可变数据结构的性能受到当前使用的同步方法(无论是基于锁还是无锁)的限制。
现代 NUMA 系统上内存管理的挑战
使用 Carrefour 优化 NUMA 系统应用
现代服务器级系统通常构建为将多个多核芯片组合在一个系统中。每个芯片都有一个本地 DRAM(动态随机存取存储器)模块;它们一起被称为一个节点。节点通过高速互连连接,系统完全一致。这意味着,对程序员透明的是,核心可以向其节点的本地内存以及其他节点的内存发出请求。关键的区别在于,远程请求将花费更长的时间,因为它们会受到更长的线路延迟的影响,并且可能必须跳转多跳才能遍历互连。
使用 Promise 的并行处理
编写协作系统的简单方法
在当今世界,有很多理由编写并发软件。提高性能和增加吞吐量的愿望导致了许多不同的异步技术。然而,所涉及的技术通常很复杂,并且是许多细微错误的根源,尤其是当它们需要共享可变状态时。如果不需要共享状态,那么这些问题可以通过更好的抽象(称为 promise)来解决。这些 promise 允许程序员将异步函数调用连接在一起,等待每个调用返回成功或失败,然后再运行链中的下一个适当函数。
实用同步原语的可扩展性技术
以性能为中心设计锁定原语
在理想世界中,应用程序有望在越来越大的系统上执行时自动扩展。然而,在实践中,不仅不会发生这种扩展,而且在更大的系统上看到性能实际上变差是很常见的。
并行编程的生产力:十年的进步
展望 X10 的设计和优势
2002 年,DARPA(国防高级研究计划局)启动了一项 HPCS(高生产力计算系统)的重大倡议。该计划的动机是相信,即将到来的并行机器的利用受到在 peta 规模上编写、调试、调整和维护软件的难度的限制。
使用锁省略扩展现有的基于锁的应用程序
锁省略使现有的基于锁的程序能够以较少软件工程工作量获得非阻塞同步和细粒度锁定的性能优势。
多线程应用程序利用不断增加的内核数量来实现高性能。然而,此类程序通常要求程序员推理多个线程之间共享的数据。程序员使用互斥锁等同步机制来确保在多个线程访问的情况下正确更新共享数据。不幸的是,这些机制会序列化线程对数据的访问并限制可扩展性。
选择非阻塞特性的平衡行为
非阻塞系统的设计要求
什么是非阻塞进展?考虑一个简单的示例,即递增多个线程之间共享的计数器 C。一种方法是通过互斥锁 L 保护递增 C 的步骤(即,acquire(L); old := C ; C := old+1; release(L);)。如果线程 P 持有 L,则不同的线程 Q 必须等待 P 释放 L,然后 Q 才能继续操作 C。也就是说,Q 被 P 阻塞。
非阻塞算法和可扩展的多核编程
探索基于锁的同步的一些替代方案
具有复杂服务质量保证的实际系统可能需要在吞吐量和延迟之间取得微妙的平衡,以满足经济高效的运营要求。商品多核和众核系统的日益普及和成本降低使得并发性和并行性对于满足苛刻的性能要求变得越来越必要。不幸的是,正确、高效且可扩展的并发软件的设计和实现通常是一项艰巨的任务。
证明非阻塞数据结构的正确性
因此,您已决定使用非阻塞数据结构,现在您需要确定其正确性。如何才能实现这一点?
非阻塞同步可以在可扩展性和实时响应方面产生惊人的结果,但代价是验证状态空间。
结构化延迟:通过拖延进行同步
我们根本没有可以强制互斥的同步机制。
开发人员通常对软件设计采取积极主动的方法,尤其是那些来自重视勤奋胜过拖延的文化的人。然而,懒惰的方法已经证明了它们的价值,例如引用计数、垃圾回收和惰性求值。这种结构化延迟采用通过拖延进行同步的形式,特别是引用计数、危害指针和 RCU(读取-复制-更新)。
软件事务内存:为何它只是一个研究玩具?
STM 的前景很可能被其开销和工作负载适用性所破坏。
TM(事务内存)是一种并发控制范例,为代码区域提供原子和隔离的执行。许多研究人员认为 TM 是解决多核处理器编程问题最有希望的解决方案之一。其最吸引人的特点是,大多数程序员只需要在本地推理共享数据访问,标记要以事务方式执行的代码区域,并让底层系统确保正确的并发执行。该模型有望提供细粒度锁定的可扩展性,同时避免锁组合的常见陷阱,例如死锁。
使用事务内存进行并行编程
虽然有时甚至编写常规的单线程程序也可能非常具有挑战性,但尝试将程序拆分为可以并行执行的多个部分会增加一整维的额外问题。事务内存借鉴了大多数程序员熟悉的事务概念,旨在解决其中一些问题并使并行编程更容易。来自 Red Hat 的 Ulrich Drepper 向我们展示了它是如何完成的。
由于过去几十年我们喜爱的单个内核的速度不再以我们喜爱的速度增长,程序员不得不寻找其他方法来提高我们日益复杂的应用程序的速度。CPU 制造商提供的功能是执行单元或 CPU 内核数量的增加。
用于并发编程的 Erlang
编程语言在处理并发性方面可以发挥什么作用?答案可以在 Erlang 中找到,Erlang 是一种从头开始为并发性设计的语言。
Erlang 是一种旨在让普通人编写、测试、部署和调试容错并发软件的语言。它于 20 世纪 80 年代末在瑞典电信公司爱立信开发,最初是用于开发用于管理电话交换机的软实时软件的平台。此后,它已开源并移植到多个通用平台,不仅在分布式互联网服务器应用程序中找到了自然的契合点,而且在图形用户界面和普通批处理应用程序中也找到了自然的契合点。
真实世界的并发
在本次对并发性如何影响现实世界从业者的观察中,Cantrill 和 Bonwick 认为,对并发性的许多焦虑是不必要的。
如果最近的微处理器发展给今天的软件从业者带来了对软件未来的些许担忧,那也是可以原谅的。虽然摩尔定律继续成立(即,晶体管密度大约每 18 个月翻一番),但由于难以解决的物理限制和实际的工程考虑,这种不断增加的密度不再用于提高时钟频率。相反,它被用来在单个 CPU 芯片上放置多个 CPU 内核。
解锁并发性
使用事务内存进行多核编程
多核架构是主流软件开发的一个转折点,因为它们迫使开发人员编写并行程序。在 Queue 之前的一篇文章中,Herb Sutter 和 James Larus 指出,“并发革命主要是一场软件革命。困难的问题不是构建多核硬件,而是以一种让主流应用程序能够从 CPU 性能持续指数增长中受益的方式对其进行编程。”在这个新的多核世界中,开发人员必须编写显式并行应用程序,这些应用程序可以利用每个后续多核世代将提供的不断增加的内核数量。
没有痛苦的线程
多线程编程不必如此令人焦虑。
当今的大部分软件都在处理多个并发任务。Web 浏览器支持多个并发 HTTP 连接,图形用户界面处理多个窗口和输入设备,Web 和 DNS 服务器处理来自大量客户端的并发连接或事务。需要处理的并发任务数量不断增加,而软件变得越来越复杂。以一种满足不断增长的可扩展性要求,同时保持足够简单、结构化和安全的方式来构建并发软件,以允许普通程序员构建日益复杂的系统,这是一项重大的工程挑战。
软件与并发革命
充分利用多核处理器的强大功能需要软件行业的新工具和新思维。
长期以来,并发性一直被吹捧为“下一个重大事件”和“未来的方向”,但在过去的 30 年中,主流软件开发一直能够忽略它。我们的并行未来终于到来了:新机器将是并行机器,这将需要我们开发软件的方式发生重大变化。本期中的介绍性文章描述了从单处理器到多核处理器(也称为 CMP)的计算机体系结构转变背后的硬件必然性。
调试并发性的试验与磨难
你可以跑,但你无法躲藏。
我们现在牢牢地坐在 21 世纪,现代程序员面临的巨大挑战既不是内存泄漏也不是类型问题(这两个问题现在都得到了有效解决),而是并发性问题。如何编写日益复杂的程序,其中并发性是首要考虑的问题。或者更危险的是,如何调试这样的野兽?这些问题让即使是最优秀的程序员也感到恐惧。