像鸟一样思考更好的并行编程

介绍

编写一个应用程序并行运行很困难,对吧?我的意思是,它一定很难,否则我们会看到各处的并行程序。我们所看到的都是平滑的并行应用程序,可以毫不费力地使用每个可用的核心。相反,多线程应用程序是例外而不是规则。

编写并行程序似乎有两个主要障碍:

  • 学习您选择的语言提供的并行编程结构和/或约定
  • 可视化您的并行程序的功能

第一项似乎很明显:休息一下,学习所选编程语言的并行功能,然后离开你 - 并行程序将从你的编译器中跳出。除非那天下午通常会变成几天,这通常会变成一段更长的时间,而你选择的语言的平行特征的含义,并发症和后果则会被驯服。

第二项似乎应该提到几乎是微不足道的。毕竟,开发一个新计划的第一步是想象它的主要组成部分以及它们将如何运作。除了我们通常将我们的新程序可视化为顺序代码组件,以便我们稍后(如果必须的话)将某些并行的东西“栓上”。正是这种思维框架使我们从一开始就走上了错误的道路。

相反,我们需要考虑像鸟这样的程序。或者说,成群的鸟儿。

背景

Avian计算项目是通过改进我们对并行编程的思考来改善并行编程的一种方式。Avian Computing鼓励我们将并行程序可视化为一群鸟,每个鸟(线程)独立并异步地执行操作,但它们同时作为一群来完成程序的目标。

我们都熟悉鸟类:它们被孵化出来,四处飞翔寻找食物,产卵并孵化自己的鸟类,然后死亡。Avian Computing将这些基本的鸟类行为转化为开发人员可以利用的编码框架来快速开发工作并行程序的原型。从构建这个工作原型获得的知识深度和见解可以简化最终软件产品的开发。

Avian计算的实现

并发资源管理器(ConcX)是用Java编写的Avian Computing的概念的免费开源实现。ConcX为用户提供了一个可以创建和配置新鸟的GUI屏幕。配置完成后,可以启动小鸟并在进度条上监控其活动。配置好的鸟群可以保存为一个“群”,可以根据需要重新加载和运行。

一旦孵化(开始),每只鸟(线)遵循标准生命周期; 它寻找食物,消化找到的任何匹配的食物,储存任何产生的食物,然后小睡一会儿。在配置的耐力时限内找不到食物的鸟类将因饥饿而死亡。活得太久的鸟会死于老年。成功吃足以满足可配置设置的鸟类将复制(重复自己)。这个标准生命周期允许使用简单自然的词汇来处理线程管理的复杂性,这些词汇几乎可以直观地理解。

ConcX依赖于Linda协调语言来从共享虚拟关联存储器(称为元组空间)中放置和检索对象。Linda起源于1986年,由Sudhir Ahuja,David Gelernter和Nicholas Carriero创作。Linda是几个主要产品的基础,包括Sun的JavaSpaces,IBM的TSpaces等等。

ConcX使用称为TupleTree的Linda元组空间的简化版本。鸟类通常配置为在TupleTree中食用和储存食物豆荚。例如,RedPod可以被配置为吃RedPods的任何鸟食用,并且将被所有其他鸟类忽略。在消化它的食物荚后,一只鸟会将加工后的物体存放回TupleTree,通常作为不同种类的食物荚(例如BluePod),以供不同的鸟类食用。

Linda(及其派生,TupleTree)提供了一个安全,简单和强大的消息传递机制。在ConcX中,TupleTree允许在用户编写的代码中没有任何特殊的代码或注意事项的情况下共享对象,因为TupleTree同步(锁定或以其他方式提供独占访问)所有与其底层数据存储交互的方法。

使TupleTree对所有锁定负责,确保鸟儿接收的任何食物吊舱(物体)完全由该鸟类拥有,而不会使用锁定和互斥等使用户代码混乱。这意味着任何接收到食物吊舱的鸟都是免费的在没有来自任何其他鸟类的争用或干扰的情况下对该食品盒进行任何改变。在做出任何必要的改变之后,该鸟可以将其食物吊舱放回TupleTree,在那里不同种类的鸟会吃它并进行改变。

平行思维的简单范例

ConcX提供的Addx场景是Avian计算概念如何生成更简单的并行代码的直接示例。Addx场景的目标是通过对每个值执行一系列数学运算来处理一系列值,直到计算出最终值并保存为止。数学运算必须始终以相同的顺序执行。

在标准的顺序编码中,我们将通过获取下一个值,执行第一个数学运算,然后第二个数学运算,然后第三个数学运算等开始可视化一个循环,直到计算出最终值为止,该值为保存,然后循环重复。看起来相对简单,并且运行速度尽可能快。其最大吞吐量取决于执行单线程的一个处理器的速度。

为了更快速地处理更多的值,需要使用多个处理器,这就是棘手的问题。正常的解决方案是创建一个线程池,每个线程执行上述的顺序代码。但是这会使处理复杂化,因为并行代码必须 确保输入值仅由一个线程处理,并且不会跳过输入值,同时确保不会发生死锁和活锁。很难想象线程可能会互相干扰的所有可能的方式 - 在客户的站点上展示了多次新的令人惊讶的失败模式。更不用说必须为每个运行时环境(笔记本电脑vs大型机等)预先配置和编译多个版本的应用程序。

Addx(Avian)解决方案配置了一个任意数量的鸟群,每只鸟只吃一种食物,并且在将其作为不同类型的食物放回之前,只对该食物执行一次数学运算。通过适当配置食物和储存的食物,保证数学操作的正确顺序。例如,Bird1吃Food1,对其执行数学运算,并将其存储为Food2。Bird2吃Food2,对它进行数学运算,并将其作为Food3存储。Bird3吃Food3,等等。这也可以更简洁地表达为:

食物1 - > Bird1 - > FOOD2 - > BIRD2 - > Food3 - > Bird3。。。.Foodn - > Birdn。

以下简化图说明了Avian并行性。

  • 在下面的生命周期1中,所有五只鸟在大约同一时间开始飞行,但只有Add1Bird找到任何食物。它对该值执行操作,然后将其作为只有Add2Bird吃的食物放回到TupleTree中。
  • 在生命周期2中,Add1Bird和Add2Bird都可以找到食物,以便他们都执行各自的操作,然后将其食物存储回TupleTree。
  • 在生命周期3中,Add1Bird,Add2Bird和Add3Bird都可以找到它们的食物种类,处理它们并将它们放回到TupleTree中。

大约第五个循环通过它们的生命周期(找到食物,消化它,储存它和午睡),所有五只鸟同时从TupleTree进食,处理它们的荚,并将它们更新的荚储存在树中。上面只画了5只鸟,但很容易想象将这个图放大到包括20只或50只或100只鸟,它们都将同时运行(飞行),所有这些都遵循相同的简单和自然的模式。就像真正的鸟类一样,如果任何鸟类寻找食物并且没有找到它,它就会等待一会儿,然后再次尝试。

重要提示:如图所示这鸟不步调一致操作的简化图。每只小鸟以其自己的个体速率生活,因为每次小睡时,它会随机选择一段时间(在可配置的范围内)。这意味着随机短时间小睡的小鸟会比随机长时间小睡的小鸟更快地完成其生命周期。随着时间的推移,午睡的时间长短会趋于平缓,所以一段时间以后会很慢。在现实生活中,Add5Bird(或任何其他鸟)开始吃之前可能需要3或10或15个周期。

配置鸟类

ConcX提供GUI屏幕来添加,配置和启动鸟类。GUI屏幕还可以在鸟儿飞行时提供动态的实时状态更新。以下屏幕截图显示了运行Addx场景的五只小鸟。屏幕右侧的进度条实时显示每只鸟的成功。每只鸟的进度条越长,鸟成功吃的次数就越多。

由于每只鸟都有用户可选择的食物类型,因此重新排列数学运算的顺序很简单。只需更改选定的食物并重新运行即可。如果任何鸟的配置不合适,它将无法找到食物,其进度条也不会增长。

“食物供应”选项卡包含食物容器进度条,可以实时动态显示可用食物容器的数量。当运行结束时,TupleTree选项卡显示其包含的食物荚的时间戳列表以及每个食物荚内容的简要摘要以及每个食物荚和哪些鸟执行的交易。

上述功能都集成到并发资源管理器中,允许您交互式地探索和开发并行程序。一旦你了解了如何将程序分解为可以并行运行的子步骤,则可以使用所选择的编程语言对应用程序进行编码。

使用代码

下面显示了Add3Bird的完整Java代码。它只有43行,几乎一半(19)的行可以是注释或空白(用于视觉分离的空白或单个花括号)。Add3Bird所需的唯一代码是afterDigestion方法的重写,它所做的只是将3添加到它找到的任何非空食品盒中。查找窗体并将其存储回树中都由BasicBird框架处理,使开发人员的工作变得更轻松。

对于上面的代码最重要的是,在这段代码中没有锁定或同步或互斥,因为它全部由TupleTree和ConcX框架处理。所有的多线程并行代码都在后台进行管理,因此用户可以专注于如何将主要任务划分为鸟类可以并行处理原子的小尺寸碎片。

虽然上述任务可能看起来过于简单,但它实际上只是更复杂场景的模板。例如,如果Add1Bird被一只抓住10毫秒声音的鸟取代并且Add2Bird被替换为对其执行快速傅里叶变换的鸟并且Add3Bird被替换为试图将所得输出与其他先前处理的结果等。如果第二只鸟跟不上第一只鸟,而不是试图修改它以加快运行速度(并且可能引入错误),那么首选的Avian解决方案就是添加另一个实例(或另外10个实例)第二只鸟。

兴趣点

Avian Computing的开发旨在鼓励用户基于天生平行的模型(例如鸟群)来形象化他们的并行程序。蜜蜂蜂群,鱼群或马群也可以作为模型,因为它们都包含多个独立运行并且异步运行的角色,同时也可以一起工作。

ConcX是由开发人员/实验者共同创建的。这是一个交互式环境,允许用户启动和停止鸟类的各种组合和配置。虽然每只鸟的飞行都有一个不断更新的进度条,以显示它的成功程度和可用的食物供应量。飞行结束后,可以检查鸡群的结果以及每只鸡的事件历史记录。

Avian Computing和ConcX实现的概念旨在成为培训轮子,帮助我们的单线程思维思考并讨论并行程序。

要详细了解Avian Computing的基本概念以及为什么我们需要帮助来思考并行程序,请访问Avian Computing网站。如果您准备尝试一下,可以从Avian网站或SourceForge下载ConcX-2.x.zip(jar文件,lib文件和flock文件)。请务必同时下载“Avian Computing入门指南”用户指南,因为它包含安装信息以及大约十几种并行方案,例如并行计算Pi,用餐哲学家Probelm,BarberShop场景等。

针对上面的技术我特意整理了一下,有很多技术不是靠几句话能讲清楚,所以干脆找朋友录制了一些视
频,很多问题其实答案很简单,但是背后的思考和逻辑不简单,要做到知其然还要知其所以然。如果想
学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以
加我的Java进阶群:744642380,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分
享给大家

原文地址:http://blog.51cto.com/13742357/2116673

时间: 2024-10-14 15:20:11

像鸟一样思考更好的并行编程的相关文章

C#中的多线程 - 并行编程 z

原文:http://www.albahari.com/threading/part5.aspx 专题:C#中的多线程 1并行编程Permalink 在这一部分,我们讨论 Framework 4.0 加入的多线程 API,它们可以充分利用多核处理器. 并行 LINQ(Parallel LINQ)或称为 PLINQ Parallel类 任务并行(task parallelism)构造 SpinLock 和 SpinWait 这些 API 可以统称为 PFX(Parallel Framework,并行

C# 并行编程 之 命令式任务并行 (.Net Framework 4.0)

此文为个人学习<C#并行编程高级教程>的笔记,总结并调试了一些文章中的代码示例. 在以后开发过程中可以加以运用. 最基本的使用,并行任务的创建 在 .Net Framework 4 中出现了Task 的概念.在以往的多线程程序中虽然使用的是thread,但大多数的时候我们还是会把业务处理划分为Task.这样更接近于人类的思考方式,毕竟Thread不能说明它和业务的关联.这里C#直接提供了task,也算是对开发者简便了一些. Task 的使用非常简单,定义工作函数,创建Task,Task开始运行

并行编程框架 ForkJoin

本文假设您已经了解一般并行编程知识,了解Java concurrent部分如ExecutorService等相关内容. 虽说是Java的ForkJoin并行框架,但不要太在意Java,其中的思想在其它语言环境也是同样适用的.因为并发编程在本质上是一样的.就好像如何找到优秀的Ruby程序员?其实要找的只是一个优秀的程序员.当然,如果语言层面直接支持相关的语义会更好. 引言 Java 语言从一开始就支持线程和并发性语义.Java5增加的并发工具又解决了一般应用程序的并发需求,Java6.Java7又

NET中的并行编程(TPL)——多线程、异步、任务和并行计算

https://masuit.com/1201 谈一谈.NET中的并行编程(TPL)——多线程.异步.任务和并行计算 懒得勤快 发表于2018-04-26 19:41:00 | 最后修改于2018-06-27 23:44:40 .NET 多线程 异步 高并发 分类:.NET开发技术 | 评论总数:0条 | 热度:2243℃ 我要编辑 写在前面: 在做了几个月的高并发项目的过程中,其实发现自己真的提升了不少,所以也想把这段时间的收获分享给大家,然后写这篇文章发现,写下来是一发不可收拾,所以这篇文章

并行编程入门

目录 1. 并行编程简介 2. MapReduce 2.1 MapReduce简介 2.2 MapReduce框架 2.3 Hadoop介绍 2.4 Hadoop基本类 2.5 Hadoop编程实例 1.并行编程简介 1.1.并行编程作用,用途 商业用途,科学计算,大数据分析 1.2.并行编程兴起原因 目前的串行编程的局限性 使用的流水线等隐式并行模式的局限性 硬件的发展 1.3.并行算法设计原则步骤 a.分析问题 b.分解问题 其中分解方法有: 数据分解 递归分解 探测性分解 推测性分解 混合

C#并行编程 z

目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 C#并行编程-线程同步原语 C#并行编程-PLINQ:声明式数据并行 背景 基于任务的程序设计.命令式数据并行和任务并行都要求能够支持并发更新的数组.列表和集合. 在.NET Framework 4 以前,为了让共享的数组.列表和集合能够被多个线程更新,需要添加复杂的代码来同步这些更新操作. 如您需要编写一个并行循环,这个循环以无序的方式向一个共享集合中添加元素,那么必须加入一个同步机制

.Net中的并行编程-4.实现高性能异步队列

上文<.Net中的并行编程-3.ConcurrentQueue实现与分析>分析了ConcurrentQueue的实现,本章就基于ConcurrentQueue实现一个高性能的异步队列,该队列主要用于实时数据流的处理并简化多线程编程模型.设计该队列时考虑以下几点需求(需求来自公司的一个实际项目): 1. 支持多线程入队出队,尽量简化多线程编程的复杂度. 2. 支持事件触发机制,数据入队时才进行处理而不是使用定时处理机制, 而且内部能阻塞消费者线程. 3. 出队时数据处理的顺序要保证和入队时是一致

第三篇:GPU 并行编程的运算架构

前言 GPU 是如何实现并行的?它实现的方式较之 CPU 的多线程又有什么分别? 本文将做一个较为细致的分析. GPU 并行计算架构 GPU 并行编程的核心在于线程,一个线程就是程序中的一个单一指令流,一个个线程组合在一起就构成了并行计算网格,成为了并行的程序,下图展示了多核 CPU 与 GPU 的计算网格: 二者的区别将在后面探讨. 下图展示了一个更为细致的 GPU 并行计算架构: 该图表示,计算网格由多个流处理器构成,每个流处理器又包含 n 多块. 下面进一步对 GPU 计算网格中的一些概念

.Net中的并行编程-6.常用优化策略

            本文是.Net中的并行编程第六篇,今天就介绍一些我在实际项目中的一些常用优化策略.      一.避免线程之间共享数据 避免线程之间共享数据主要是因为锁的问题,无论什么粒度的锁,最好的线程之间同步方式就是不加锁,这个地方主要措施就是找出数据之间的哪个地方需要共享数据和不需要共享数据的地方,再设计上避免多线程之间共享数据. 在以前做过的某项目,开始时设计的方案: 开始设计时所有的数据都放入到了公共队列,然后队列通知多个线程去处理数据,队列采用互斥锁保证线程同步,造成的结果就