转帖 multi thread

多核程序设计相关之openMP,cpu亲和力《转帖》
2009-09-07 15:28

OpenMP是作为共享存储标准而问世的。它是为在多处理机上编写并行程序而设计的一个应用

编程接口。它包括一套编译指导语句和一个用来支持它的函数库。
  目前双核心的 CPU 当道,AMD 的 Athlon64x2、Intel 的 Pentium-D、Core Duo,以及

即将上市的 Core 2 Duo,俨然将成为下一代电脑的主流(尤其是超低价的 Pentium D,绝

对是现阶段 C/P 值极高的双核心 CPU)。但是双核心有什麼用呢?
  对於一般单一执行绪(single thread)的程式,多核心的处理器并没有办法提升它的

处理效能;不过对於多执行绪(multi thread)的程式,就可以透过不同的核心同时计算,

来达到加速的目的了!简单的例子,以单执行绪的程式来说,一件事做一次要十秒的话,要

做十次,都丢给同一颗核心做的话,自然就是 10 秒 * 10 次,也就是 100 秒了;但是以

多执行绪的程式来说,它可以把这一件事,分给两颗核心各自做,每颗核心各做 5 次,所

以所需要的时间就只需要 50 秒!
  当然,多执行绪的程式实际上没这麼简单。在工作的切割、结合上,也是要多花时间的

,所以在现实中,即使最佳状况,双核心的效能也不会是 1 + 1 = 2 这样的理想化。除此

之外,也不是所有工作都是可以切割的!很多工作是有关联性的,这样如果直接切割给不同

的处理核心各自去平行运算,出来的结果是肯定有问题的。而且,多执行绪的程式在编写、

维护上,也都比单一执行绪的程式复杂上不少。
  不过,如果电脑本身是多处理器、多核心处理器,或是处理器拥有像 Intel Hyper-

Threading Technology 这类的能在同一个时间处理多个执行绪的功能的话,那把各自独立

的工作由单一执行绪改成多执行绪,在执行的效率上,大多还是会有增进的!
  ---------------------------------------------------------------------------

-----
[编辑本段]多执行绪的程式
  写程式的时候该怎麼去写多执行绪的程式呢?一般的方法,就是真的利用 thread 的控

制,去实际在程式中去产生其他的 thread 来处理。像 POSIX Threads 这套 library,就

是用来产生、控制执行绪的函式库。而像 Microsoft VisualStudio 2005 中,也有提供控

制 thread 的功能。这种方法,大多就是产生多个 thread,而再由主要的 thread 把工作

拆开,分给各 thread 去运算,最後再由主要的 thread 回收结果、整合。
  但是,实际上要去控制 thread 是满麻烦的~在程式的编写上,也会复杂不少;而如果

我们只是想要把一些简单的回圈平行化处理,用 thread library 来控制,实在有点杀鸡用

牛刀的感觉。这时候,用 Open MP 就简单多了!OpenMP 是一种能透过高阶指令,很简单地

将程式平行化、多执行绪化的 API;在最简单的情形,甚至可以只加一行指令,就可以将回

圈内的程式平行化处理了!
  ---------------------------------------------------------------------------

-----
[编辑本段]OpenMP 的基本使用
  要在 Visual C++ 2005 中使用 openMP 其实不难,只要将 Project 的 Properties 中

C/C++ 里 Language 的 OpenMP Support 开启(参数为 /openmp),就可以让 VC++2005 在

编译时支援 OpenMP 的语法了;而在使用到 OpenMP 的档案,则需要先 include OpenMP 的

header file : omp.h。
  而要将 for 回圈平行化处理,该怎麼做呢?非常简单,只要在前面加上一行
  #pragma omp parallel for
  就够了!
  也可以实际用一段简单的程式,来弄清楚它的运作方式。
  #include
  #include
  void Test(int n) {
  for (int i = 0; i < 10000; ++i)
  {
  //do nothing, just waste time
  }
  printf("%d, ", n);
  }
  int main(int argc, char* argv[])
  {
  for (int i = 0; i < 10; ++i)
  Test(i);
  system("pause");
  }
  上面的程式,在 main() 是一个很简单的回圈,跑十次,每次都会呼叫 Test() 这个函

氏,并把是回圈的执行次数(i)传进 Test() 并列印出来。想当然耳,它的结果会是:
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,而如果想利用 OpenMP 把 mian() 里面的回圈平行化

处理呢?只需要修改成下面的样子:
  #include
  #include
  #include
  void Test (int n) {
  for (int i = 0; i < 10000; ++i) {
  //do nothing, just waste time
  }
  printf("%d, ", n);
  }
  int main(int argc, char* argv[]) {
  #pragma omp parallel for
   for (int i = 0; i < 10; ++i)
  Test( i );
  system("pause");
  }
  够简单吧?重头到尾,只加了两行(红色部分)!而执行後,可以发现结果也变了!
  0, 5, 1, 6, 2, 7, 3, 8, 4, 9,可以从结果很明显的发现,他没有照著 0 到 9 的顺

序跑了!而上面的顺序怎麼来的?其实很简单,OpenMP 只是把回圈 0 - 9 共十个步骤,拆

成 0 - 4, 5 - 9 两部份,丢给不同的执行绪去跑,所以数字才会出现这样交错性的输出~
  而要怎麼确定真的有跑多执行绪呢?如果本来有多处理器、多核心处理器或有 Hyper

Thread 的话,一个单执行绪程式,最多只会把一颗核心的使用量吃完;像比如说在

Pentium 4 HT 上跑,单一执行绪的程式,在工作管理员中看到的 CPU 使用率最多就是 50%

。而利用 OpenMP 把回圈进行平行化处理後,就可以在执行廻圈时,把两颗核心的 CPU 都

榨光了!也就是工作管理员可以看到 CPU 使用率是 100%。

 

以上内容 来自百度百科

 

 

PU Affinity (CPU亲合力)

CPU亲合力就是指在Linux系统中能够将一个或多个进程绑定到一个或多个处理器上运行.
一个进程的CPU亲合力掩码决定了该进程将在哪个或哪几个CPU上运行.在一个多处理器系统

中,设置CPU亲合力的掩码可能会获得更好的性能.
一个CPU的亲合力掩码用一个cpu_set_t结构体来表示一个CPU集合,下面的几个宏分别对这个

掩码集进行操作:
CPU_ZERO() 清空一个集合
CPU_SET()与CPU_CLR()分别对将一个给定的CPU号加到一个集合或者从一个集合中去掉.
CPU_ISSET()检查一个CPU号是否在这个集合中.
其实这几个的用法与select()函数那几个调用差不多.
下面两个函数就是最主要的了:
sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask)
该函数设置进程为pid的这个进程,让它运行在mask所设定的CPU上.如果pid的值为0,则表示

指定的是当前进程,使当前进程运行在mask所设定的那些CPU上.第二个参数cpusetsize是

mask所指定的数的长度.通常设定为sizeof(cpu_set_t).如果当前pid所指定的CPU此时没有

运行在mask所指定的任意一个CPU上,则该指定的进程会从其它CPU上迁移到mask的指定的

一个CPU上运行.
sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask)
该函数获得pid所指示的进程的CPU位掩码,并将该掩码返回到mask所指向的结构中.即获得指

定pid当前可以运行在哪些CPU上.同样,如果pid的值为0.也表示的是当前进程.

这几个宏与函数的具体用法前面已经有讲解.

关于cpu_set_t的定义

[Copy to clipboard] [ - ]CODE:
# define __CPU_SETSIZE 1024
# define __NCPUBITS     (8 * sizeof (__cpu_mask))

typedef unsigned long int __cpu_mask;

# define __CPUELT(cpu) ((cpu) / __NCPUBITS)
# define __CPUMASK(cpu) ((__cpu_mask) 1 << ((cpu) % __NCPUBITS))

typedef struct
{
__cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS];
} cpu_set_t;

# define __CPU_ZERO(cpusetp) /
do {                                                                        /
    unsigned int __i;                                                         /
    cpu_set_t *__arr = (cpusetp);                                             /
    for (__i = 0; __i < sizeof (cpu_set_t) / sizeof (__cpu_mask); ++__i)      /
      __arr->__bits[__i] = 0;                                                 /
} while (0)
# define __CPU_SET(cpu, cpusetp) /
((cpusetp)->__bits[__CPUELT (cpu)] |= __CPUMASK (cpu))
# define __CPU_CLR(cpu, cpusetp) /
((cpusetp)->__bits[__CPUELT (cpu)] &= ~__CPUMASK (cpu))
# define __CPU_ISSET(cpu, cpusetp) /
(((cpusetp)->__bits[__CPUELT (cpu)] & __CPUMASK (cpu)) != 0)
在我的机器上sizeof(cpu_set_t)的大小为128,即一共有1024位.第一位代表一个CPU号.某一

位为1则表示某进程可以运行在该位所代表的cpu上.例如
CPU_SET(1, &mask);
则mask所对应的第2位被设置为1.
此时如果printf("%d/n", mask.__bits[0]);就打印出2.表示第2位被置为1了.

 

具体我是参考man sched_setaffinity文档中的函数的.
然后再参考了一下IBM的 developerWorks上的一个讲解.
http://www.ibm.com/developerworks/cn/linux/l-affinity.html


现在多CPU的趋势越来越大了. 有时候为了更好地操作机器, 需要将某个进程绑定到具体的

CPU上去. 下面给出了一个进程绑定到具体的CPU上去的一个例子.


cpu.c

[Copy to clipboard] [ - ]CODE:
#include
#include
#include
#include
#include

#define __USE_GNU
#include
#include
#include

int main(int argc, char* argv[])
{
        int num = sysconf(_SC_NPROCESSORS_CONF);
        int created_thread = 0;
        int myid;
        int i;
        int j = 0;

        cpu_set_t mask;
        cpu_set_t get;

        if (argc != 2)
        {
                printf("usage : ./cpu num/n");
                exit(1);
        }

        myid = atoi(argv[1]);

        printf("system has %i processor(s). /n", num);

        CPU_ZERO(&mask);
        CPU_SET(myid, &mask);

        if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
        {
                printf("warning: could not set CPU affinity, continuing.../n");
        }
        while (1)
        {

                CPU_ZERO(&get);
                if (sched_getaffinity(0, sizeof(get), &get) == -1)
                {
                        printf("warning: cound not get cpu affinity,

continuing.../n");
                }
                for (i = 0; i < num; i++)
                {
                        if (CPU_ISSET(i, &get))
                        {
                                printf("this process %d is running processor :

%d/n",getpid(), i);
                        }
                }
        }
        return 0;
}
下面是在两个终端分别执行了./cpu 0 ./cpu 2 后得到的结果. 效果比较明显.

QUOTE:
Cpu0 : 5.3%us, 5.3%sy, 0.0%ni, 87.4%id, 0.0%wa, 0.0%hi, 2.0%si, 0.0%st
Cpu1 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu2 : 5.0%us, 12.2%sy, 0.0%ni, 82.8%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu3 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu4 : 0.0%us, 0.0%sy, 0.0%ni, 99.7%id, 0.3%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu5 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu6 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu7 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st

那个是在运行top命令之后再按1就出现了对每个CPU的统计.
us 用户空间占用CPU百分比
sy 内核空间占用CPU百分比
ni 用户进程空间内改变过优先级的进程占用CPU百分比
id 空闲CPU百分比
wa 等待输入输出的CPU时间百分比
后面两个我也不是太清楚,麻烦清楚的朋友说一下.

使用#define __USE_GNU是为了使用CPU_SET()等宏. 具体在/usr/include/sched.h中有如下

的定义.
因为我开始编写的时候没有加这个宏, 出现了错误, 后来查看了这个定义才知道要使用这个

东西.

[Copy to clipboard] [ - ]CODE:
#ifdef __USE_GNU
/* Access macros for `cpu_set'. */
#define CPU_SETSIZE __CPU_SETSIZE
#define CPU_SET(cpu, cpusetp)   __CPU_SET (cpu, cpusetp)
#define CPU_CLR(cpu, cpusetp)   __CPU_CLR (cpu, cpusetp)
#define CPU_ISSET(cpu, cpusetp) __CPU_ISSET (cpu, cpusetp)
#define CPU_ZERO(cpusetp)       __CPU_ZERO (cpusetp)


/* Set the CPU affinity for a task */
extern int sched_setaffinity (__pid_t __pid, size_t __cpusetsize,
                              __const cpu_set_t *__cpuset) __THROW;

/* Get the CPU affinity for a task */
extern int sched_getaffinity (__pid_t __pid, size_t __cpusetsize,
                              cpu_set_t *__cpuset) __THROW;
#endif

 

以上转自http://linux.chinaunix.net/bbs/viewthread.php?tid=904906&extra=page%3D1%26amp%3Bfilter%3Ddigest

你可能感兴趣的:(转帖 multi thread)