[使用Intel® Thread Profiler 实例] 重复使用已创建的线程以减少系统的额外开销

intel_zhenyuwang 2008-05-09 01:58:50
加精
在多核平台上开发程序,我们主张把子任务并行化。这样需要创建多个进程。问题是,是不是线程越多越好呢?请看下面的示例。

/*计算集合粒子通过成对相互作用的势能*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <windows.h>

#define NPARTS 1000
#define NITER 21
#define DIMS 3
#define NUM_THREADS 2

//int rand( void );
DWORD WINAPI computePot(LPVOID);
void initPositions(void);
void updatePositions(void);

double r[DIMS][NPARTS];
int bounds[2][NUM_THREADS];
double pot;
double gPot[NUM_THREADS];

int main() {
int i, j;
HANDLE tHandle[NUM_THREADS];
int tNum[NUM_THREADS];

for (i=0; i<NUM_THREADS; i++) {
bounds[0][i] = i * (NPARTS/NUM_THREADS);
bounds[1][i] = (i+1) * (NPARTS/NUM_THREADS);
}
bounds[1][NUM_THREADS-1] = NPARTS;

initPositions();
updatePositions();

for( i=0; i<NITER; i++ ) {
pot = 0.0;
for (j=0; j<NUM_THREADS; j++) {
tNum[j] = j;
tHandle[j] = CreateThread(NULL, 0, computePot, &tNum[j], 0, NULL);
}
WaitForMultipleObjects(NUM_THREADS, tHandle, TRUE, INFINITE);

for (j=0; j<NUM_THREADS; j++) {
pot += gPot[j];
}
if (i%10 == 0) printf("%5d: Potential: %10.3f\n", i, pot);
updatePositions();
}
}


void initPositions() {
int i, j;

for( i=0; i<DIMS; i++ )
for( j=0; j<NPARTS; j++ )
r[i][j] = 0.5 + ( (double) rand() / (double) RAND_MAX );
}


void updatePositions() {
int i, j;

for( i=0; i<DIMS; i++ )
for( j=0; j<NPARTS; j++ )
r[i][j] -= 0.5 + ( (double) rand() / (double) RAND_MAX );
}


DWORD WINAPI computePot(LPVOID pArg) {
int i, j, start, end, tid;
double lPot = 0.0;
double distx, disty, distz, dist;

tid = *(int *)pArg;
start = bounds[0][tid];
end = bounds[1][tid];

for( i=start; i<end; i++ ) {
for( j=0; j<i-1; j++ ) {
distx = pow( (r[0][j] - r[0][i]), 2 );
disty = pow( (r[1][j] - r[1][i]), 2 );
distz = pow( (r[2][j] - r[2][i]), 2 );
dist = sqrt( distx + disty + distz );
lPot += 1.0 / dist;
}
}
gPot[tid] = lPot;
return 0;
}

我们创建了42个子任务(进程)加上一个主线程,但是并行的效果并不理想10.82秒(我是运行在双核的机器上,同一时刻最多二个任务并行)。从中可以看出,我们虽然试图创建多个线程并试图并行,但是由于资源的有限性,只能有二个子任务同时并行。而这么多的线程创建及终止,毕竟也消耗了额外的系统资源。

下面是使用Intel(R) Thread Profiler 看到的结果。
Figure-1

那么如何在现有的基础上对代码进行改进呢?
DWORD WINAPI tPoolComputePot(LPVOID); // 增加线程池的控制程序
int done = 0;
HANDLE bSignal[NUM_THREADS]; // 信号量用作计算开始
HANDLE eSignal[NUM_THREADS]; // 信号量用作计算结束

改写main函数:
int main() {
int i, j;
HANDLE tHandle[NUM_THREADS];
int tNum[NUM_THREADS];

for (i=0; i<NUM_THREADS; i++) {
bounds[0][i] = i * (NPARTS/NUM_THREADS);
bounds[1][i] = (i+1) * (NPARTS/NUM_THREADS);
bSignal[i] = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset
eSignal[i] = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset
}
bounds[1][NUM_THREADS-1] = NPARTS;

for (j=0; j<NUM_THREADS; j++) {
tNum[j] = j;
tHandle[j] = CreateThread(NULL, 0, tPoolComputePot, &tNum[j], 0, NULL);
}

initPositions();
updatePositions();

for( i=0; i<NITER; i++ ) {
WaitForMultipleObjects(NUM_THREADS, eSignal, TRUE, INFINITE); //上次已结束?

pot = 0.0;
for (j=0; j<NUM_THREADS; j++) {
pot += gPot[j];
}
if (i%10 == 0) printf("%5d: Potential: %10.3f\n", i, pot);
updatePositions();
}
done = 1; //全部处理完
for (j=0; j<NUM_THREADS; j++)
SetEvent(bSignal[i]);

WaitForMultipleObjects(NUM_THREADS, tHandle, TRUE, INFINITE);
}

// 修改updatePositions 函数
void updatePositions() {
// 保持原来工作,增加下面内容-发出”可以”工作信号
for (j=0; j<NUM_THREADS; j++)
SetEvent(bSignal[j]);
}

// 增加新的计算控制函数
DWORD WINAPI tPoolComputePot(LPVOID pArg) {
int tid = *(int *)pArg;

while (!done) {
WaitForSingleObject(bSignal[tid], INFINITE); //等待开始信号
computePot(tid);
SetEvent(eSignal[tid]); //发出结束信号
}
return 0;
}

从上面的修改可以看到,子任务的线程从42个变成了2个。原来的任务还是在这二个线程中反复运行,这样,我们我们省去了线程的创建和终止所占用的系统时间。注意,我们用四个信号量去管理二个线程的任务调度。二种方法,那一种更好些呢?可以用Intel® Thread Profiler 去做一下测量,见下图。
一个很重要的指标:Total Critical Path Time.
还可以深入观察到每一个线程和对象的数据。

Figure-2
...全文
942 42 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
42 条回复
切换为时间正序
请发表友善的回复…
发表回复
qgdbr08 2009-05-16
  • 打赏
  • 举报
回复
谢谢楼主~ 这个视频我之前有看过,做的很生动,可是,我还是不知道怎么使用Thread Profiler查看线程的状态..帮助文档里提到Thread Tab,可我没找到...
就像在安装附带的samples里的CodeExamplesGurde.pdf里给出的图示
qgdbr08 2009-05-16
  • 打赏
  • 举报
回复
谢谢楼主~ 这个视频我之前有看过,做的很生动,可是,我还是不知道怎么使用Thread Profiler查看线程的状态..帮助文档里提到Thread Tab,可我没找到...
就像在安装附带的samples里的CodeExampl里给出的图示,esGurde.pdf
intel_zhenyuwang 2009-05-15
  • 打赏
  • 举报
回复
[Quote=引用 39 楼 qgdbr08 的回复:]
上边的所有图示我都看不到~ 对于Intel thread profiler 我只看到两个tab:Profile View和Summary 1,至于其他的比如Thread Tab,我找不着,也看不见每个线程相应的数据...如果能把整个帖子做成视频教学,效果是不是更加直观呢?呵呵...我好像要求太多了...再次感谢楼主!
[/Quote]
Video
qgdbr08 2009-05-14
  • 打赏
  • 举报
回复
楼主辛苦了~
如果能给出使用openmp实现对应功能的程序就更好了~ 呵呵
qgdbr08 2009-05-14
  • 打赏
  • 举报
回复
上边的所有图示我都看不到~ 对于Intel thread profiler 我只看到两个tab:Profile View和Summary 1,至于其他的比如Thread Tab,我找不着,也看不见每个线程相应的数据...如果能把整个帖子做成视频教学,效果是不是更加直观呢?呵呵...我好像要求太多了...再次感谢楼主!
hetao25226 2009-05-12
  • 打赏
  • 举报
回复
了解,了解,兴趣
bigpretty 2009-05-12
  • 打赏
  • 举报
回复
顶 顶了
fw176170847 2009-05-12
  • 打赏
  • 举报
回复
老实说 ,只看懂了多线程,顶一个 ,学习
y456789 2009-05-12
  • 打赏
  • 举报
回复
thread profiler曾经有用过,但因为日常工作较忙,30天的试用期感觉太少了,可能一个月里也就用那么一个星期,不知道能否增加试用的时间?
xiaoallenge 2009-05-12
  • 打赏
  • 举报
回复
不错,学习中!
cool8511 2009-05-11
  • 打赏
  • 举报
回复
多线程主要还是增强用户体验吧,当线程多余CPU核个数的时候,我怀疑对于运行性能上是否真的有很大提高
intel_zhenyuwang 2009-05-11
  • 打赏
  • 举报
回复
Timeline界面上,每个线程在每个时刻可能处在三个不同状态:活动,等待,自璇。
另外还有一个线程到另一个线程的Transition,可以查看相应的源代码。

这里有产品的简要介绍可供下载。

Intel® Thread Profiler 3.1 for Windows* – Documentation
xwd88888 2009-05-11
  • 打赏
  • 举报
回复
非常好,学习中
amwlsai 2009-05-11
  • 打赏
  • 举报
回复
顶。。。
liaosy198259 2009-05-11
  • 打赏
  • 举报
回复
顶下 学习中
xiaoxiong5227 2009-05-11
  • 打赏
  • 举报
回复
ytsmtm 2009-05-10
  • 打赏
  • 举报
回复
Timeline窗口 怎么查看每一时刻线程的状态呢?主要看这个窗口里的什么参数呢?我很迷惑!我一直不知道怎么看这个窗口的数据!
另外楼主有没有比较好的关于TP使用的文档啊?能帮我发一份吗?我现在在学习这些东西,感觉有点困难,只能用基本的性能数据,很多都不清楚!我的邮箱是:kangtao-520@163.com
谢谢你了!
寻梦928 2008-12-15
  • 打赏
  • 举报
回复
非常好,学习...
laxila 2008-09-30
  • 打赏
  • 举报
回复
非常好,学习过了,谢谢楼主呀!!!
majiajun_no_5 2008-09-30
  • 打赏
  • 举报
回复
获益匪浅
加载更多回复(22)

567

社区成员

发帖
与我相关
我的任务
社区描述
英特尔® 边缘计算,聚焦于边缘计算、AI、IoT等领域,为开发者提供丰富的开发资源、创新技术、解决方案与行业活动。
社区管理员
  • 英特尔技术社区
  • shere_lin
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧