|
Posted on 2012-12-18 13:56 鑫龙 阅读(562) 评论(0) 编辑 收藏 引用 所属分类: linux多线程专题
实验环境:
CPU: 双核 Intel(R) Xeon(R) CPU 5130 @ 2.00GHz,内存:1G 系统:Red Hat Enterprise Linux ES release 4 (Nahant Update 4) 内核:2.6.9-42.ELsmp
实验程序:
singlethread.c
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #include <pthread.h>
- #include <unistd.h>
- #define ORANGE_MAX_VALUE 1000000
- #define APPLE_MAX_VALUE 100000000
- #define MSECOND 1000000
- struct apple
- {
- unsigned long long a;
- unsigned long long b;
- };
- struct orange
- {
- int a[ORANGE_MAX_VALUE];
- int b[ORANGE_MAX_VALUE];
- };
- int main()
- {
- struct apple test;
- struct orange test1={{0},{0}};
-
- unsigned long long sum=0,index=0;
- struct timeval tpstart,tpend;
- float timeuse;
-
- test.a=0;
- test.b=0;
-
- gettimeofday(&tpstart,NULL);
-
- for(sum=0;sum<APPLE_MAX_VALUE;sum++)
- {
- test.a+=sum;
- test.b+=sum;
- }
-
- sum=0;
- for(index=0;index<ORANGE_MAX_VALUE;index++)
- {
- sum+=test1.a[index]+test1.b[index];
- }
-
- gettimeofday(&tpend,NULL);
-
- timeuse=MSECOND*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;
- timeuse/=MSECOND;
- printf("main thread Used Time:%f\n",timeuse);
-
- printf("a = %llu,b = %llu,sum=%llu\n",test.a,test.b,sum);
-
- return 0;
- }
dualrhread.c- #include <sys/time.h>
- #include <pthread.h>
- #include<stdlib.h>
- #include<stdio.h>
- #include<sys/types.h>
- #include<sys/sysinfo.h>
- #include<unistd.h>
- #define __USE_GNU
- #include<sched.h>
- #include<ctype.h>
- #include<string.h>
- #include <sys/syscall.h> /*此头必须带上*/
- pid_t gettid()
- {
- return syscall(SYS_gettid);
- }
- #define ORANGE_MAX_VALUE 1000000
- #define APPLE_MAX_VALUE 100000000
- #define MSECOND 1000000
- struct apple
- {
- unsigned long long a;
- unsigned long long b;
- };
- struct orange
- {
- int a[ORANGE_MAX_VALUE];
- int b[ORANGE_MAX_VALUE];
- };
- struct apple test;
- struct orange test1;
- int cpu_nums;
- cpu_set_t mask;
- //cpu_set_t get;
-
- inline int set_cpu(int i)
- {
- __CPU_ZERO(&mask);
-
- if(2 <= cpu_nums)
- {
- __CPU_SET(i,&mask);
-
- if(-1 == sched_setaffinity(gettid(),sizeof(&mask),&mask))
- {
- return -1;
- }
- }
- return 0;
- }
- void* add(void* x)
- {
- int i;
- if(-1 == set_cpu(1))
- {
- return NULL;
- }
- /*
- __CPU_ZERO(&get);
- if (sched_getaffinity(0, sizeof(get), &get) == -1)//获取线程CPU亲和力
- {
- printf("warning: cound not get thread affinity, continuing...\n");
- }
-
- for (i = 0; i < 2; i++)
- {
- if (__CPU_ISSET(i, &get))//判断线程与哪个CPU有亲和力
- {
- printf("this thread %d is running processor : %d\n", i,i);
- }
- }
- */
- unsigned long long sum=0,index=0;
- for(sum=0;sum<APPLE_MAX_VALUE;sum++)
- {
- ((struct apple *)x)->a += sum;
- ((struct apple *)x)->b += sum;
- }
-
- return NULL;
- }
-
- int main ()
- {
-
- pthread_t ThreadA;
- unsigned long long sum=0,index=0;
- struct timeval tpstart,tpend;
- float timeuse;
- int i;
-
- test.a=0;
- test.b=0;
-
- cpu_nums = sysconf(_SC_NPROCESSORS_CONF);
-
- if(-1 == set_cpu(0))
- {
- return -1;
- }
-
- gettimeofday(&tpstart,NULL);
-
- pthread_create(&ThreadA,NULL,add,&test);
-
- /*
- __CPU_ZERO(&get);
- if (sched_getaffinity(0, sizeof(get), &get) == -1)//获取线程CPU亲和力
- {
- printf("warning: cound not get thread affinity, continuing...\n");
- }
-
- for (i = 0; i < 2; i++)
- {
- if (__CPU_ISSET(i, &get))//判断线程与哪个CPU有亲和力
- {
- printf("this thread %d is running processor : %d\n", i,i);
- }
- }
- */
- for(index=0;index<ORANGE_MAX_VALUE;index++)
- {
- sum += test1.a[index]+test1.b[index];
- }
-
- pthread_join(ThreadA,NULL);
- gettimeofday(&tpend,NULL);
-
- timeuse=MSECOND*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;
- timeuse/=MSECOND;
- printf("dual thread Used Time:%f\n",timeuse);
-
- printf("a = %llu,b = %llu,sum=%llu\n",test.a,test.b,sum);
-
- return 0;
- }
threethread.c- #include <sys/time.h>
- #include <pthread.h>
- #include<stdlib.h>
- #include<stdio.h>
- #include<sys/types.h>
- #include<sys/sysinfo.h>
- #include<unistd.h>
- #define __USE_GNU
- #include<sched.h>
- #include<ctype.h>
- #include<string.h>
- #include <sys/syscall.h> /*此头必须带上*/
- pid_t gettid()
- {
- return syscall(SYS_gettid);
- }
- #define ORANGE_MAX_VALUE 1000000
- #define APPLE_MAX_VALUE 100000000
- #define MSECOND 1000000
- struct apple
- {
- unsigned long long a;
- //char c[128]; /*32,64,128*/
- unsigned long long b;
- //pthread_rwlock_t rwLock;
- };
- struct orange
- {
- int a[ORANGE_MAX_VALUE];
- int b[ORANGE_MAX_VALUE];
- };
- struct apple test;
- struct orange test1;
- int cpu_nums;
- cpu_set_t mask;
-
- inline int set_cpu(int i)
- {
- __CPU_ZERO(&mask);
-
- if(2 <= cpu_nums)
- {
- __CPU_SET(i,&mask);
-
- if(-1 == sched_setaffinity(gettid(),sizeof(&mask),&mask))
- {
- return -1;
- }
- }
- return 0;
- }
- void* addx(void* x)
- {
- /*
- if(-1 == set_cpu(0))
- {
- return NULL;
- }
- */
- unsigned long long sum=0,index=0;
- //pthread_rwlock_wrlock(&((struct apple *)x)->rwLock);
- for(sum=0;sum<APPLE_MAX_VALUE;sum++)
- {
- ((struct apple *)x)->a += sum;
- }
- //pthread_rwlock_unlock(&((struct apple *)x)->rwLock);
-
- return NULL;
- }
- void* addy(void* y)
- {
- /*
- if(-1 == set_cpu(1))
- {
- return NULL;
- }
- */
- unsigned long long sum=0,index=0;
- //pthread_rwlock_wrlock(&((struct apple *)y)->rwLock);
- for(sum=0;sum<APPLE_MAX_VALUE;sum++)
- {
- ((struct apple *)y)->b += sum;
- }
- //pthread_rwlock_unlock(&((struct apple *)y)->rwLock);
-
- return NULL;
- }
- int main ()
- {
- pthread_t ThreadA,ThreadB;
- unsigned long long sum=0,index=0;
- struct timeval tpstart,tpend;
- float timeuse;
- /*
- cpu_nums = sysconf(_SC_NPROCESSORS_CONF);
-
- if(-1 == set_cpu(0))
- {
- return -1;
- }
- */
- gettimeofday(&tpstart,NULL);
-
- pthread_create(&ThreadA,NULL,addx,&test);
- pthread_create(&ThreadB,NULL,addy,&test);
- for(index=0;index<ORANGE_MAX_VALUE;index++)
- {
- sum+=test1.a[index]+test1.b[index];
- }
-
- pthread_join(ThreadA,NULL);
- pthread_join(ThreadB,NULL);
-
- gettimeofday(&tpend,NULL);
-
- timeuse=MSECOND*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;
- timeuse/=MSECOND;
- printf("thread thread,Used Time:%f\n",timeuse);
-
- printf("a = %llu,b = %llu,sum=%llu\n",test.a,test.b,sum);
-
- return 0;
- }
本文采用采用 gettimeofday() 来获取系统时钟(system clock)时间,可以精确到微秒。本文平均值算法采用的是去掉一个最大值去掉一个最小值,然后平均
一、单核 单、多线程效率比较
单核 | | 第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 第6次 | 第7次 | 第8次 | 第9次 | 第10次 | 平均 | 单线程 | 0.951274 | 0.951883 | 0.951347 | 0.950893 | 0.810648 | 0.810705 | 0.951196 | 0.951343 | 0.811413 | 0.950892 | 0,916133 | 双线程 | 0.989287 | 0.986391 | 0.986576 | 0.985043 | 0.986335 | 0.985320 | 0.986790 | 0.986837 | 0.985602 | 0.986535 | 0.986298 | 三线程加锁 | 1.318721 | 1.318701 | 1.319036 | 1.318652 | 1.318614 | 1.318923 | 1.318636 | 1.318687 | 1.319026 | 1.318933 | 1.318785 | 三线程不加锁 | 1.318658 | 1.318766 | 1.318570 | 1.318894 | 1.318588 | 1.318658 | 1.318958 | 1.318709 | 1.318689 | 1.319020 | 1.318740 |
为什么多线程会比单线程更耗时呢?其原因就在于,线程启停以及线程上下文切换都会引起额外的开销,所以消耗的时间比单线程多。 二、双核---单线程、多线程、优化后多线程效率比较双核三线程不加锁 | | 第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 第6次 | 第7次 | 第8次 | 第9次 | 第10次 | Best | 单线程 | 0.947490 | 0.948153 | 0.950648 | 0.950412 | 0.950422 | 0.950407 | 0.810345 | 0.950386 | 0.947501 | 0.947513 | 0.949036 | 双线程 | 0.983432 | 0.980912 | 0.976860 | 0.975621 | 0.976582 | 0.978240 | 0.975736 | 0.981523 | 0.979724 | 0.981337 | 0.978864 | 三线程加锁 | 1.310762 | 1.309590 | 1.308914 | 1.309287 | 1.310924 | 1.310837 | 1.307147 | 1.311698 | 1.311861 | 1.309007 | 1.310127 | 三线程不加锁 | 1.431863 | 1.422951 | 1.472916 | 1.482321 | 1.477823 | 1.477272 | 1.423854 | 1.423412 | 1.422267 | 1.423243 | 1.444167 | 三线程加锁Cache32 | 1.310174 | 1.308985 | 1.311462 | 1.311574 | 1.309590 | 1.311329 | 1.311602 | 1.311154 | 1.307573 | 1.310447 | 1.310589 | 三线程不加锁Cache32 | 0.664234 | 0.664244 | 0.664186 | 0.664264 | 0.664317 | 0.664234 | 0.662658 | 0.662176 | 0.662164 | 0.662159 | 0.663520 |
为什么三线程不加锁比三线程加锁还慢呢?当两个线程写入同一个 cache 的不同部分时,会互相竞争该 cache 行,也就是写后写的问题。下文会详细分析。 为什么三线程加锁Cache32比三线程不加锁Cache32比慢几乎一倍呢?
其原因也很简单,那把读写锁就是罪魁祸首。实际情况并不是并行执行,反而成了串行执行。 三、针对 Cache 的优化 在串行程序设计过程中,为了节约带宽或者存储空间,比较直接的方法,就是对数据结构做一些针对性的设计,将数据压缩 (pack) 的更紧凑,减少数据的移动,以此来提高程序的性能。但在多核多线程程序中,这种方法往往有时会适得其反。 数据不仅在执行核和存储器之间移动,还会在执行核之间传输。根据数据相关性,其中有两种读写模式会涉及到数据的移动:写后读和写后写 ,因为这两种模式会引发数据的竞争,表面上是并行执行,但实际只能串行执行,进而影响到性能。 处理器交换的最小单元是 cache 行,或称 cache 块。在多核体系中,对于不共享 cache 的架构来说,两个独立的 cache 在需要读取同一 cache 行时,会共享该 cache 行,如果在其中一个 cache 中,该 cache 行被写入,而在另一个 cache 中该 cache 行被读取,那么即使读写的地址不相交,也需要在这两个 cache 之间移动数据,这就被称为 cache 伪共享,导致执行核必须在存储总线上来回传递这个 cache 行,这种现象被称为“乒乓效应”。 同样地,当两个线程写入同一个 cache 的不同部分时,也会互相竞争该 cache 行,也就是写后写的问题。上文曾提到,不加锁的方案反而比加锁的方案更慢,就是互相竞争 cache 的原因。 在 X86 机器上,某些处理器的一个 cache 行是64字节,具体可以参看 Intel 的参考手册。 既然不加锁三线程方案的瓶颈在于 cache,那么让 apple 的两个成员 a 和 b 位于不同的 cache 行中,效率会有所提高吗? 修改后的代码片断如下:
- struct apple
- {
- unsigned long long a;
- char c[128]; /*32,64,128*/
- unsigned long long b;
- };
|