[转] 管理处理器的亲和性(affinity)

标签: 管理 处理器 亲和性 | 发表时间:2011-09-07 15:16 | 作者:heiyeshuwu chuang
出处:http://blog.csdn.net/heiyeshuwu


作者:Eli Dow
来源:http://www.ibm.com/developerworks/cn/linux/l-affinity.html 

简单地说,CPU 亲和性(affinity) 就是进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性。Linux 内核进程调度器天生就具有被称为软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。

2.6 版本的 Linux 内核还包含了一种机制,它让开发人员可以编程实现 硬 CPU 亲和性(affinity)。这意味着应用程序可以显式地指定进程在哪个(或哪些)处理器上运行。

什么是 Linux 内核硬亲和性(affinity)?

在 Linux 内核中,所有的进程都有一个相关的数据结构,称为 task_struct。这个结构非常重要,原因有很多;其中与 亲和性(affinity)相关度最高的是cpus_allowed 位掩码。这个位掩码由 n 位组成,与系统中的 n 个逻辑处理器一一对应。 具有 4 个物理 CPU 的系统可以有 4 位。如果这些 CPU 都启用了超线程,那么这个系统就有一个 8 位的位掩码。

如果为给定的进程设置了给定的位,那么这个进程就可以在相关的 CPU 上运行。因此,如果一个进程可以在任何 CPU 上运行,并且能够根据需要在处理器之间进行迁移,那么位掩码就全是 1。实际上,这就是 Linux 中进程的缺省状态。

Linux 内核 API 提供了一些方法,让用户可以修改位掩码或查看当前的位掩码:

  • sched_set_affinity() (用来修改位掩码)
  • sched_get_affinity() (用来查看当前的位掩码)

注意,cpu_affinity 会被传递给子线程,因此应该适当地调用 sched_set_affinity


回页首

为什么应该使用硬亲和性(affinity)?

通常 Linux 内核都可以很好地对进程进行调度,在应该运行的地方运行进程(这就是说,在可用的处理器上运行并获得很好的整体性能)。内核包含了一些用来检测 CPU 之间任务负载迁移的算法,可以启用进程迁移来降低繁忙的处理器的压力。

一般情况下,在应用程序中只需使用缺省的调度器行为。然而,您可能会希望修改这些缺省行为以实现性能的优化。让我们来看一下使用硬亲和性(affinity) 的 3 个原因。

原因 1. 有大量计算要做

基于大量计算的情形通常出现在科学和理论计算中,但是通用领域的计算也可能出现这种情况。一个常见的标志是您发现自己的应用程序要在多处理器的机器上花费大量的计算时间。

原因 2. 您在测试复杂的应用程序

测试复杂软件是我们对内核的亲和性(affinity)技术感兴趣的另外一个原因。考虑一个需要进行线性可伸缩性测试的应用程序。有些产品声明可以在 使用更多硬件 时执行得更好。

我们不用购买多台机器(为每种处理器配置都购买一台机器),而是可以:

  • 购买一台多处理器的机器
  • 不断增加分配的处理器
  • 测量每秒的事务数
  • 评估结果的可伸缩性

如果应用程序随着 CPU 的增加可以线性地伸缩,那么每秒事务数和 CPU 个数之间应该会是线性的关系(例如斜线图 —— 请参阅下一节的内容)。这样建模可以确定应用程序是否可以有效地使用底层硬件。

Amdahl 法则

Amdahl 法则是有关使用并行处理器来解决问题相对于只使用一个串行处理器来解决问题的加速比的法则。加速比(Speedup) 等于串行执行(只使用一个处理器)的时间除以程序并行执行(使用多个处理器)的时间:

      T(1)
S = ------
      T(j)
      

其中 T(j) 是在使用 j 个处理器执行程序时所花费的时间。

Amdahl 法则说明这种加速比在现实中可能并不会发生,但是可以非常接近于该值。对于通常情况来说,我们可以推论出每个程序都有一些串行的组件。随着问题集不断变大,串行组件最终会在优化解决方案时间方面达到一个上限。

Amdahl 法则在希望保持高 CPU 缓存命中率时尤其重要。如果一个给定的进程迁移到其他地方去了,那么它就失去了利用 CPU 缓存的优势。实际上,如果正在使用的 CPU 需要为自己缓存一些特殊的数据,那么所有其他 CPU 都会使这些数据在自己的缓存中失效。

因此,如果有多个线程都需要相同的数据,那么将这些线程绑定到一个特定的 CPU 上是非常有意义的,这样就确保它们可以访问相同的缓存数据(或者至少可以提高缓存的命中率)。否则,这些线程可能会在不同的 CPU 上执行,这样会频繁地使其他缓存项失效。

原因 3. 您正在运行时间敏感的、决定性的进程

我们对 CPU 亲和性(affinity)感兴趣的最后一个原因是实时(对时间敏感的)进程。例如,您可能会希望使用硬亲和性(affinity)来指定一个 8 路主机上的某个处理器,而同时允许其他 7 个处理器处理所有普通的系统调度。这种做法确保长时间运行、对时间敏感的应用程序可以得到运行,同时可以允许其他应用程序独占其余的计算资源。

下面的样例应用程序显示了这是如何工作的。


回页首

如何利用硬亲和性(affinity)

现在让我们来设计一个程序,它可以让 Linux 系统非常繁忙。可以使用前面介绍的系统调用和另外一些用来说明系统中有多少处理器的 API 来构建这个应用程序。实际上,我们的目标是编写这样一个程序:它可以让系统中的每个处理器都繁忙几秒钟。可以从后面的“下载”一节中下载样例程序


清单 1. 让处理器繁忙
                
/* This method will create threads, then bind each to its own cpu. */
bool do_cpu_stress(int numthreads)
{
   int ret = TRUE;
   int created_thread = 0;
   /* We need a thread for each cpu we have... */
   while ( created_thread < numthreads - 1 )
   {
      int mypid = fork();
      if (mypid == 0) /* Child process */
       {
          printf("\tCreating Child Thread: #%i\n", created_thread);
          break;
      }
      else /* Only parent executes this */
      {
          /* Continue looping until we spawned enough threads! */ ;
          created_thread++;
      }
   }
   /* NOTE: All threads execute code from here down! */

正如您可以看到的一样,这段代码只是通过 fork 调用简单地创建一组线程。每个线程都执行这个方法中后面的代码。现在我们让每个线程都将亲和性(affinity)设置为自己的 CPU。


清单 2. 为每个线程设置 CPU 亲和性(affinity)
                
   cpu_set_t mask;
   /* CPU_ZERO initializes all the bits in the mask to zero. */
        CPU_ZERO( &mask );
   /* CPU_SET sets only the bit corresponding to cpu. */
        CPU_SET( created_thread, &mask );
   /* sched_setaffinity returns 0 in success */
        if( sched_setaffinity( 0, sizeof(mask), &mask ) == -1 )
   {
      printf("WARNING: Could not set CPU Affinity, continuing...\n");
   }

如果程序可以执行到这儿,那么我们的线程就已经设置了自己的亲和性(affinity)。调用 sched_setaffinity 会设置由pid 所引用的进程的 CPU 亲和性(affinity)掩码。如果 pid 为 0,那么就使用当前进程。

亲和性(affinity)掩码是使用在 mask 中存储的位掩码来表示的。最低位对应于系统中的第一个逻辑处理器,而最高位则对应于系统中最后一个逻辑处理器。

每个设置的位都对应一个可以合法调度的 CPU,而未设置的位则对应一个不可调度的 CPU。换而言之,进程都被绑定了,只能在那些对应位被设置了的处理器上运行。通常,掩码中的所有位都被置位了。这些线程的亲和性(affinity)都会传递给从它们派生的子进程中。

注意不应该直接修改位掩码。应该使用下面的宏。虽然在我们的例子中并没有全部使用这些宏,但是在本文中还是详细列出了这些宏,您在自己的程序中可能需要这些宏。


清单 3. 间接修改位掩码的宏
                
void CPU_ZERO (cpu_set_t *set)
这个宏对 CPU 集 set 进行初始化,将其设置为空集。
void CPU_SET (int cpu, cpu_set_t *set)
这个宏将 cpu 加入 CPU 集 set 中。
void CPU_CLR (int cpu, cpu_set_t *set)
这个宏将 cpu 从 CPU 集 set 中删除。
int CPU_ISSET (int cpu, const cpu_set_t *set)
如果 cpu 是 CPU 集 set 的一员,这个宏就返回一个非零值(true),否则就返回零(false)。

对于本文来说,样例代码会继续让每个线程都执行某些计算量较大的操作。


清单 4. 每个线程都执行一个计算敏感的操作
                
    /* Now we have a single thread bound to each cpu on the system */
    int computation_res = do_cpu_expensive_op(41);
    cpu_set_t mycpuid;
    sched_getaffinity(0, sizeof(mycpuid), &mycpuid);
    if ( check_cpu_expensive_op(computation_res) )
    {
      printf("SUCCESS: Thread completed, and PASSED integrity check!\n",
         mycpuid);
      ret = TRUE;
    }
    else
    {
      printf("FAILURE: Thread failed integrity check!\n",
         mycpuid);
      ret = FALSE;
    }
   return ret;
}

现在您已经了解了在 Linux 2.6 版本的内核中设置 CPU 亲和性(affinity)的基本知识。接下来,我们使用一个 main 程序来封装这些方法,它使用一个用户指定的参数来说明要让多少个 CPU 繁忙。我们可以使用另外一个方法来确定系统中有多少个处理器:

int NUM_PROCS = sysconf(_SC_NPROCESSORS_CONF);

这个方法让程序能够自己确定要让多少个处理器保持繁忙,例如缺省让所有的处理器都处于繁忙状态,并允许用户指定系统中实际处理器范围的一个子集。


回页首

运行样例程序

当运行前面介绍的 样例程序 时,可以使用很多工具来查看 CPU 是否是繁忙的。如果只是简单地进行测试,可以使用 Linux 命令 top。在运行 top 命令时按下 “1” 键,可以看到每个 CPU 执行进程所占用的百分比。


回页首

结束语

这个样例程序虽然非常简单,但是它却展示了使用 Linux 内核中实现的硬亲和性(affinity)的基本知识。(任何使用这段代码的应用程序都无疑会做一些更有意义的事情。)了解了 CPU 亲和性(affinity)内核 API 的基本知识,您就可以从复杂的应用程序中榨取出最后一点儿性能了。 

作者:heiyeshuwu 发表于2011-9-7 23:16:08 原文链接
阅读:211 评论:0 查看评论

相关 [管理 处理器 亲和性] 推荐:

[转] 管理处理器的亲和性(affinity)

- chuang - heiyeluren的blog(黑夜路人的开源世界)
来源:http://www.ibm.com/developerworks/cn/linux/l-affinity.html . 简单地说,CPU 亲和性(affinity) 就是进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性. Linux 内核进程调度器天生就具有被称为软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移.

Windows 8 任务管理器新特性:逻辑处理器热图

- 屁清新健脑 - cnBeta.COM
Windows 8开发博客今天又公布了微软下一代操作系统的任务管理器新特性. 在当前Windows 7的任务管理器中,人们很难监控每个处理器在处理繁重任务时的资源消耗情况,而在Windows 8中一切变得非常简单,每一个逻辑处理器的工作状况都逃不出人的视野,微软用方框+文字的形式展现出系统的工作状况,最高可达640个逻辑处理器,热图的尺寸和布局会自动调整,如果有必要还会创建一个滚动条.

ARM发布Cortex-A7处理器

- tinda - Solidot
手机先生 写道 "ARM公司宣布了新的Cortex A7处理器,声称是迄今最节能的处理器,与Cortex A8处理器相比,性能提升但功耗只有后者的1/5. ARM表示,2013-2014年期间,新款Cortex A7将用于100美元以下的入门级智能手机,其性能表现相当于目前市售500美元级别高端智能手机.

ARM宣布了64位处理器ARMv8

- Woooon - Solidot
ARM宣布了64位处理器ARMv8,最后一种主流处理器架构终于跨入了64位时代. 同时惠普宣布将推出ARM服务器. ARMv8包含AArch64和AArch32两种主要执行状态:AArch64引入了新的64位指令集,AArch32兼容现有的ARMv7架构. 《连线》的一篇评论认为,ARM的64位扩展纯粹是增加可寻址存储器大小,并没有同时提高自身性能,而增加寄存器大小会对耗电产生负面影响.

HBASE 协处理器入门(转载)

- - 数据库 - ITeye博客
如果要统对hbase中的数据,进行某种统计,比如统计某个字段最大值,统计满足某种条件的记录数,统计各种记录特点,并按照记录特点分类(类似于sql的group by)~. 常规的做法就是把hbase中整个表的数据scan出来,或者稍微环保一点,加一个filter,进行一些初步的过滤(对于rowcounter来说,就加了FirstKeyOnlyFilter),但是这么做来说还是会有很大的副作用,比如占用大量的网络带宽(当标级别到达千万级别,亿级别之后)尤为明显,RPC的量也是不容小觑的.

管理

- - 人月神话的BLOG
对于中小企业而言现在管理上欠缺的不是人治或者说儒家佛家等东方管理思想,而真正欠缺的是西方法治的科学管理方法. 现在很多中小企业花很多钱去听什么东方管理思想的培训是误入歧途,东西方管理思想需要融合,但是基础还是科学的管理方法和模式. 而在这个里面最重要的仍然是流程管理,知识管理,质量管理,项目管理这些内容,而不是简单的纯管理.

寻宝处理器 《大话处理器》新书出炉(序一与网友评论)

- Ken - 弯曲评论
序一  寻宝处理器的引人入胜之旅.          当出版社的编辑介绍万木杨的这本书给我时,我对书名《大话处理器》是有一定担心的,其一:处理器和计算机的发展几十年来风起云涌,其间有天才的创新、看似偶然的分叉和囿于商业市场考量的成功与失败,一部技术发展史绝不比波谲云诡的社会史逊色. 一部“大话”处理器的书会不会流于一部围绕处理器发展种种轶事的大话技术史.

晶体管3D化,英特尔重新发明微处理器

- Ant - Solidot
芯片巨人宣布,即将推出的22nm处理器将采用全新的技术设计晶体管. 英特尔重点介绍了下一代22nm处理器的新特性——三闸极晶体管技术. 晶体管主要是充当电流开关,以极快的速度用电信号控制流出的电流. 传统“平面”晶体管有三大组件:源极(Source)、栅极(Gate)和漏极(Drain). 你可以把源极和漏极想象成标准电流插座的两个插孔,当在插孔之间连上一根导线,形成一个封闭回路,电流就能流动.

完美运行SNES模拟器需要3GHz处理器

- 嘉德豉油公司 - Solidot
模拟器玩家都所体会,让模拟器“Just Work”并不是太困难的事情,然而让模拟器游戏能完美运行却仿佛登天,在模拟游戏中常常会遭遇各种大大小小故障,大多数都与模拟不完美有关,比如图像渲染或频数起伏巨大. 1990年代的SNES(超级任天堂)模拟器用25MHz的处理器即可运行,然而要完美却需要3GHz处理器.

Samsung Galaxy Tab 7.7 发布,配备 1.4GHz 处理器、Android 3.2、HSPA+

- Benny - Engadget 中国版
大家引颈以待的 Samsung Unpacked 发表会上,厂方终于都发表了名字早已曝光的 Galaxy Tab 7.7. 机如其名,Galaxy Tab 7.7 就是使用一块 7.7 寸屏幕(分辨率为 1280 x 800),但让人高兴的是材质使用了 SuperAMOLED Plus. 系统当然是最新的 Android 3.2 Honeycomb,也当然有 Samsung 自家的 TOUCHWiZ 4.0.