深入理解并行编程-分割和同步设计(四)

原文链接    作者:paul    译者:谢宝友,鲁阳,陈渝

设计模式与锁粒度

图1.1:设计模式与锁粒度

图1.1是不同程度同步粒度的图形表示。每一种同步粒度都用一节内容来描述。下面几节主要关注锁,不过其他几种同步方式也有类似的粒度问题。

1.1. 串行程序

Intel处理器的MIPS时钟频率变化趋势

图1.2:Intel处理器的MIPS/时钟频率变化趋势

如果程序在单处理器上运行的足够快,并且不与其他进程、线程或者中断处理程序发生交互,那么您可以将代码中所有的同步原语删掉,远离它们所带来的开销和复杂性。好多年前曾有人争论摩尔定律最终会让所有程序都变得如此。但是,随着2003年以来Intel CPU的CPU MIPS和时钟频率增长速度的停止,见图1.2,此后要增加性能,就必须提高程序的并行化程度。关于是否这种趋势会导致一块芯片上集成几千个CPU的争论不会很快停息,但是考虑到Paul是在一台双核笔记本上敲下这句话的,SMP的寿命极有可能比你我都长。另一个需要注意的地方是以太网的带宽持续增长,如图1.3所示。这种增长会导致多线程服务器的产生,这样才能有效处理通信载荷。

以太网带宽 v.s. Intel x86处理器的性能

图1.3:以太网带宽 v.s. Intel x86处理器的性能

请注意,这并不意味这您应该在每个程序中使用多线程方式编程。我再一次说明,如果一个程序在单处理器上运行的很好,那么您就从SMP同步原语的开销和复杂性中解脱出来吧。图1.4中哈希表查找代码的简单之美强调了这一点。


01 struct hash_table
02 
03{
04 
05 long nbuckets;
06 
07 struct node **buckets;
08 
09};
10 
11 typedef struct node {
12 
13 unsigned long key;
14 
15 struct node *next;
16 
17} node_t;
18 
19 int hash_search(struct hash_table *h, long key)
20 
21{
22 
23 struct node *cur;
24 
25cur = h->buckets[key % h->nbuckets];
26 
27 while (cur != NULL) {
28 
29 if (cur->key >= key) {
30 
31 return (cur->key == key);
32 
33}
34 
35cur = cur->next;
36 
37}
38 
39 return 0;
40 
41}

图1.4:串行版的哈希表搜索算法

1.2. 代码锁

代码锁是最简单的设计,只使用全局锁。在已有的程序上使用代码锁,可以很容易的让程序可以在多处理器上运行。如果程序只有一个共享资源,那么代码锁的性能是最优的。但是,许多较大且复杂的程序会在临界区上执行许多次,这就让代码锁的扩展性大大受限。


01spinlock_t hash_lock;
02 
03 struct hash_table
04 
05{
06 
07 long nbuckets;
08 
09 struct node **buckets;
10 
11};
12 
13 typedef struct node {
14 
15 unsigned long key;
16 
17 struct node *next;
18 
19} node_t;
20 
21 int hash_search(struct hash_table *h, long key)
22 
23{
24 
25 struct node *cur;
26 
27 int retval;
28 
29spin_lock(&hash_lock);
30 
31cur = h->buckets[key % h->nbuckets];
32 
33 while (cur != NULL) {
34 
35 if (cur->key >= key) {
36 
37retval = (cur->key == key);
38 
39spin_unlock(&hash_lock);
40 
41 return retval;
42 
43}
44 
45cur = cur->next;
46 
47}
48 
49spin_unlock(&hash_lock);
50 
51 return 0;
52 
53}

图1.5:基于代码锁的哈希表搜索算法

因此,您最好在只有一小段执行时间在临界区程序,或者对扩展性要求不高的程序上使用代码锁。这种情况下,代码锁可以让程序相对简单,和单线程版本类似,如图1.5所示。但是,和图1.4相比,hash_search()从简单的一行return变成了3行语句,因为在返回前需要释放锁。

图1.6:锁竞争

并且,代码锁尤其容易引起“锁竞争”,一种多个CPU并发访问同一把锁的情况。照顾一群小孩子(或者像小孩子一样的老人)的SMP程序员肯定能马上意识到某样东西只有一个的危险,如图1.6所示。

该问题的一种解决办法是下节描述的“数据锁”。

1.3. 数据锁


01 struct hash_table
02{
03 long nbuckets;
04 struct bucket **buckets;
05};
06 
07 struct bucket {
08spinlock_t bucket_lock;
09node_t *list_head;
10};
11 
12 typedef struct node {
13 unsigned long key;
14 struct node *next;
15} node_t;
16 
17 int hash_search(struct hash_table *h, long key)
18{
19 struct bucket *bp;
20 struct node *cur;
21 int retval;
22 
23bp = h->buckets[key % h->nbuckets];
24spin_lock(&bp->bucket_lock);
25cur = bp->list_head;
26 while (cur != NULL) {
27 if (cur->key >= key) {
28retval = (cur->key == key);
29spin_unlock(&bp->hash_lock);
30 return retval;
31}
32cur = cur->next;
33}
34spin_unlock(&bp->hash_lock);
35 return 0;
36}

图1.7:基于数据锁的哈希表搜索算法

许多数据结构都可以分割,数据结构的每个部分带有一把自己的锁。这样虽然每个部分一次只能执行一个临界区,但是数据结构的各个部分形成的临界区就可以并行执行了。在锁竞争必须降低时,和同步开销不是主要局限时,可以使用数据锁。数据锁通过将一块过大的临界区分散到各个小的临界区来减少锁竞争,比如,维护哈希表中的per-hash-bucket临界区,如图1.7所示。不过这种扩展性的增强带来的是复杂性的提高,增加了额外的数据结构struct bucket。

数据锁

图1.8:数据锁

和图1.6中所示的紧张局面不同,数据锁带来了和谐,见图1.8——在并行程序中,这总是意味着性能和可扩展性的提升。基于这种原因,Sequent在它的DYNIX和DYNIX/ptx操作系统中大量使用了数据锁[BK85][Inm85][Gar90][Dov90][MD92][MG92][MS93]。

不过,那些照顾过小孩子的人可以证明,再细心的照料也不能保证一切风平浪静。同样的情况也适用于SMP程序。比如,Linux内核维护了一种文件和目录的缓存(叫做“dcache”)。该缓存中的每个条目都有一把自己的锁,但是对应根目录的条目和它的直接后代相较于其他条目更容易被遍历到。这将导致许多CPU竞争这些热门条目的锁,就像图1.9中所示的情景。

数据锁出现问题

图1.9:数据锁出现问题

在许多情况下,可以设计算法来减少数据冲突的次数,某些情况下甚至可以完全消灭冲突(像Linux内核中的dcache一样[MSS04])。数据锁通常用于分割像哈希表一样的数据结构,也适用于每个条目用某个数据结构的实例表示这种情况。2.6.17内核的task list就是后者的例子,每个任务结构都有一把自己的proc_lock锁。

在动态分配结构中,数据锁的关键挑战是如何保证在已经获取锁的情况下结构本身是否存在。图1.7中的代码通过将锁放入静态分配并且永不释放的哈希桶,解决了上述挑战。但是,这种手法不适用于哈希表大小可变的情况,所以锁也需要动态分配。在这种情况,还需要一些手段来阻止哈希桶在锁被获取后这段时间内释放。

小问题1.1:当结构的锁被获取时,如何防止结构被释放呢?

1.4. 数据所有权

数据所有权方法按照线程或者CPU的个数分割数据结构,这样每个线程/CPU都可以在不需任何同步开销的情况下访问属于它的子集。但是如果线程A希望访问另一个线程B的数据,那么线程A是无法直接做到这一点的。取而代之的是,线程A需要先与线程B通信,这样线程B以线程A的名义执行操作,或者,另一种方法,将数据迁移到线程A上来。

数据所有权看起来很神秘,但是却应用得十分频繁。

1. 任何只能被一个CPU或者一个线程访问的变量(C或者C++中的auto变量)都属于这个CPU或者这个进程。

2. 用户接口的实例拥有对应的用户上下文。这在与并行数据库引擎交互的应用程序中十分常见,这让并行引擎看起来就像顺序程序一样。这样的应用程序拥有用户接口和他当前的操作。显式的并行化只在数据库引擎内部可见。

3. 参数模拟通常授予每个线程一段特定的参数区间,以此达到某种程度的并行化。

如果共享比较多,线程或者CPU间的通信会带来较大的复杂性和通信开销。不仅如此,如果使用的最多的数据正好被一个CPU拥有,那么这个CPU就成为了“热点”,有时就会导致图1.9中的类似情况。不过,在不需要共享的情况下,数据所有权可以达到理想性能,代码也可以像图1.4中所示的顺序程序例子一样简单。最坏情况通常被称为“尴尬的并行化”,而最好情况,则像图1.8中所示一样。

另一个数据所有权的重要实例发生在数据是只读时,这种情况下,所有线程可以通过复制来“拥有”数据。

1.5锁粒度与性能

本节以一种数学上的同步效率的视角,将视线投向锁粒度和性能。对数学不敢兴趣的读者可以跳过本节。

本节的方法是用一种关于同步机制效率的粗略队列模型,该机制只操作一个共享的全局变量,基于M/M/1队列。M/M/1队列。

(本节未翻译)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/261581.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

string类的实现(构造函数,析构函数,运算符重载)

String类的代码:class String { public:String(char* str""){_str new char[strlen(str) 1];strcpy(_str, str);}String(const String& str){_str new char[strlen(str._str) 1];strcpy(_str, str._str);}~String(){delete[] _str;}String& o…

html网页设计一个简单的用户登录页面

结果 代码 login.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>用户登录页面</title><link rel"stylesheet" href"./css/login.css"><script src"j…

ip聚合简介

练习一 本题选择&#xff1a;D 解释如下&#xff1a; 人事部 205.67.159.1110 0000 /27 培训部 205.67.159.1101 0000 /28 销售部 205.67.159.1100 0000 /28 先是培训部与销售部做IP聚合&#xff08;只有网络前缀位数相同才可以做ip聚合&#xff09; 保留相同的位数&…

微信公众平台消息接口开发(34)桃花运测试

微信公众平台开发 微信公众平台开发者 微信公众平台开发模式 桃花运 作者&#xff1a;方倍工作室 原文&#xff1a;http://www.cnblogs.com/txw1958/archive/2013/06/06/weixin-if34-peach-blossom-luck.html 桃花运&#xff0c;一般指得到异性缘的运气。而这种运气又常常蕴涵在…

sharepoint 2013 个人站点母版

最近做了个项目&#xff0c;&#xff0c;sharepoint 个人站点要求定制&#xff0c;&#xff0c;搞了好久不知引用的模板 在何位置&#xff0c;查了好多资料还是没有办法解决&#xff0c;&#xff0c;经过不懈的努力&#xff0c;终于找到了&#xff0c;现在记录下&#xff0c;做…

telnet不是内部或外部命令解决方法

在使用window系统在使用telnet命令时&#xff0c;会出现“telnet不是内部或外部命令”的错误。 这是因为windows默认没有开启telnet client 开启window的telnet客户端功能&#xff0c;命令就可以使用了。 1.打开控制面板 2.选择程序 3.选择启用或关闭windows功能 4.将telnet…

C# 自定义箭头组件

C#自定义箭头组件&#xff0c;效果如图&#xff1a; 实现的功能&#xff1a; 1&#xff09; 箭头方向属性左、右、上、下&#xff1b; 2&#xff09; 颜色渐变&#xff0c;且颜色任意调整&#xff1b; 3&#xff09; 箭头大小位置任意调整&#xff1b; 4&#xff09; 其他。 主…

Android的debug.keystore拒绝访问导致的生成异常及解决方案

为什么80%的码农都做不了架构师&#xff1f;>>> 构建Android应用程序的时候输出异常:[apkbuilder] keytool 错误: java.io.FileNotFoundException: C:\Users\my\.android\debug.keystore(拒绝访问.) 导致BUILD FAILED. ##异常原因: Android要求所有的应用程序必须有…

C语言猜数字游戏

程序代码 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<windows.h> #include<stdlib.h> #include<time.h> void menu() {printf("***********************\n");printf("** …

动态代理,动态代理设计模式 ,JDK动态代理,cglib动态代理

为什么80%的码农都做不了架构师&#xff1f;>>> 一&#xff1a;在看此篇代码示例前&#xff0c;先看静态代理&#xff0c; 链接地址&#xff1a;http://my.oschina.net/dyyweb/blog/656760 &#xff08;代码示例&#xff09; 二&#xff1a;JDK动态代理 动态…

C语言扫雷游戏简单实现

文章目录前言一、代码思路二、代码实现1.引入库2.具体代码见以下链接&#xff0c;免费下载&#xff0c;无需慌张3.运行结果前言 本篇文章为使用C语言的easyX库函数实现扫雷小游戏 一、代码思路 1.设置扫雷地图 用一个二维数组表示扫雷地图 初始化二维数组 埋雷&#xff0c;-1…

VS2010中C#添加图片(资源)

做工具栏的时候要用到图片。图标这样的东西从文件夹里导入显得有些山寨。VS的图形化操作很方便。但是我们的程序要动态载入图标。所以不能拖拽了~ 下面是添加图片的方法&#xff1a; 1》右击项目 》 属性 选择资源选项卡 如果没有资源的话&#xff0c;显示右上图。点击创建一个…

字符串左旋问题及判断一个字符串是否由另一个字符串左旋得到

字符串左旋问题 问题描述 左旋字符串中的k个字符。例如 ABCD左旋一个字符得到BCDA &#xff0c;ABCD左旋两个字符得到CDAB 解法一&#xff1a;暴力破解法 先左旋一个字符 将字符串首个字符保存在temp中 字符串其余字符向左移动一个单位将temp中保存的字符放到字符串结尾 重复…

浏览器是如何工作的系列:渲染引擎

渲染引擎的功能就是渲染&#xff0c;在浏览器上显示请求的内容。 默认情况下&#xff0c;渲染引擎可以显示HTML和XML文档和图像。他也可以显示其他类型的插件(浏览器扩展)。例如显示PDF使用PDF浏览器插件。 我们将用一个特殊的章节来讨论插件和扩展。在这个章节中&#xff0c;我…

富丽的SUSE Linux 10.3(1)

作者: hr127 出自: http://www.linuxdiyf.com版权声明&#xff1a; 原创作品&#xff0c;容许转载&#xff0c;转载时请务必以超链接编制标明文章 原始出处 、作者信息和本声明。不然将追究执法责任。 转载于:https://www.cnblogs.com/zgqjymx/archive/2011/03/07/1976023.htm…

matlab实现同态滤波

定义 一幅图像可看成由两部分组成&#xff0c;即 fi代表随空间位置不同的亮度&#xff08;Illumination&#xff09;分量&#xff0c;其特点是缓慢变化&#xff0c;集中在图像的低频部分。fr代表景物反射到人眼的反射&#xff08;Reflectance&#xff09;分量&#xff0c;其特…

WordPress Mail Subscribe List插件‘sml_name’参数HTML注入漏洞

漏洞名称&#xff1a;WordPress Mail Subscribe List插件‘sml_name’参数HTML注入漏洞CNNVD编号&#xff1a;CNNVD-201306-205发布时间&#xff1a;2013-06-14更新时间&#xff1a;2013-06-14危害等级&#xff1a; 漏洞类型&#xff1a;输入验证威胁类型&#xff1a;远程CVE编…

大摩维持浩大游戏“增持”评级

网易科技讯 3月3日动静&#xff0c;摩根士丹利往日宣布投资陈诉&#xff0c;指出浩大游戏第四季度业绩凌驾预期&#xff0c;具有多个利好要素&#xff0c;另外该公司在Mochi Media平台方面的极力也值得垂青。因此&#xff0c;摩根士丹利维持浩大游戏“增持”的评级。以下为陈诉…

数字图像处理频域滤波实现低通与高通滤波(包含matlab代码)

低通滤波器 理想低通滤波 作用&#xff1a;保留频谱图中圆内低频分量&#xff0c;截断频谱图中圆外高频分量函数表示&#xff1a; 假设频谱中心在 (M/2,N/2)处&#xff0c;则任意频谱成分(u,v) 到中心&#xff08;原点&#xff09;的距离D(u,v) 定义为&#xff1a; D0为低通滤…

jQuery以Post方式发送请求并获取返回的文件供下载!

用ajax请求文件下载当然是可以的&#xff0c;不用有返回值&#xff0c;代码差不多是这样&#xff1a; try{string FileName ".//doc//[大家网]Beginning.ASP.NET.2.0.E-Commerce.in.C#.2005.From.Novice.to.Professional[www.TopSage.com].pdf";FileName ".//…