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

原文链接    作者: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,一经查实,立即删除!

相关文章

XHTML标签的嵌套规则

XHTML 的标签有许多:div、ul、li、dl、dt、dd、h1~h6、p、a、addressa、span、 strong……我们在运用这些标签搭建页面结构的时候,是可以将它们无限嵌套的,但是,嵌套也需要有一定规则,不能任由网站设计师的个人习惯胡乱…

python爬虫实现网页采集器

import requests #导入requests模块 #UA:User-Agnet:请求载体身份标识 #UA检测:各大门户网站的服务器都会检测请求载体的身份标识,如果请求载体的身份标识为某一款服务器, #则该请求为正常请求,如果请求载体的身份标识…

mongodb备份oplog_MongoDB 备份(mongodump)与恢复(mongorestore)

MongoDB 备份(mongodump)与恢复(mongorestore)MongoDB数据备份在Mongodb中我们使用mongodump命令来备份MongoDB数据。该命令可以导出所有数据到指定目录中。mongodump命令可以通过参数指定导出的数据量级转存的服务器。语法mongodump命令脚本语法如下:>mongodump…

[转]MVC中如何使用RDLC报表

本文转自:http://www.cnblogs.com/BlueWoods/archive/2009/04/27/1444311.html 在用MVC开发项目的时候遇到了一个问题,如何运用RDLC报表? 首先想到的是在Controller返回DataSet,然后在View绑定值,但是遇到一个异常:...…

关于javascript作用域

今天一个挚友给我出了道javascript笔试题,代码如下: function test(){var a b 2; } test(); alert(b); alert(a);我看到这段代码,我笑了,很自信的回答道,会报错,因为a,b都是局部变量,所以a an…

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…

python url模块介绍_python 中 urlparse 模块介绍

urlparse模块主要是用于解析url中的参数 对url按照一定格式进行 拆分或拼接1.urlparse.urlparse将url分为6个部分&#xff0c;返回一个包含6个字符串项目的元组&#xff1a;协议、位置、路径、参数、查询、片段。import urlparseurl_change urlparse.urlparse(https://i.cnbl…

欢迎光临CAX软件二次开发开源社区!

欢迎光临CAX软件二次开发开源社区&#xff01; http://uucax.uueasy.com转载于:https://www.cnblogs.com/uucax/archive/2011/02/20/1959265.html

ORA-20446: The owner of the job is not registered ORA-06512: at SYSMAN.MGMT

Login as sysman user to the database and execute the following :execute MGMT_USER.MAKE_EM_USER(ARCER);//标红处为需要导出导入权限的用户的用户名 备注:假如你没有开启SYSMAN用户&#xff0c;自行开启&#xff1b;转载于:https://www.cnblogs.com/arcer/archive/2013/06…

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; 保留相同的位数&…

css编写的技巧效果总结

1. 垂直对齐 如果你之前遇到过这个问题&#xff0c;你就应该知道它是多么的烦人&#xff0c;幸运的是&#xff0c;现在你可以使用CSS3变换来解决这个问题&#xff1a; .vc{ position: relative; top: 50%; -webkit-transform: translateY(-50%); -o-transform: translateY(-50%…

微信公众平台消息接口开发(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…

java文件读写操作指定编码格式[转]

读文件&#xff1a; BufferedReader 从字符输入流中读取文本&#xff0c;缓冲各个字符&#xff0c;从而提供字符、数组和行的高效读取。 可以指定缓冲区的大小&#xff0c;或者可使用默认的大小。大多数情况下&#xff0c;默认值就足够大了。 通常&#xff0c;Reader 所作的每个…

bean覆盖 springboot_SpringBoot中如何进行Bean配置

在控制器MessageController中注入IMessageService&#xff1a;package com.gwolf.controller;import ch.qos.logback.core.net.SyslogOutputStream;import com.gwolf.service.IMessageService;import com.gwolf.util.controller.AbstractBaseController;import org.springframe…

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("** …