个人对英特尔线程挑战赛的一些意见

zm0011 2008-02-13 09:00:02
我参加了英特尔线程挑战赛,ID是ZM0011。关于生命游戏问题,我也阅读并分析过其他一些选手的源程序,并在双核计算机上做过实际测试。今天看到了比赛的结果,得分低的有点令人意外。现在我对这个比赛的客观性和公正性表示怀疑,目前已有数百名选手参加比赛,在三个星期的时间内,评论们是否认真考察过每一个选手提交的程序?评分的标准到底到底是什么?为什么不公布好的作品源码并作简要的分析,以便其他选手相互学习参考,提高技术?另外我想说明,这次比赛得分最高的程序是有缺陷的,可以从算法的角度充分论证这一点,也可以构造一份测试数据来暴露这个缺陷。这并不是想通过攻击别人的作品来抬高自己,事实上,我们总是从别人好的程序中能学到更好的方法,同时也能发现自己代码的不足。但是,这个差距悬赏的评分结果从技术的角度来看,是不能令人信服的!我相信每个选手都是出于对INTEL技术的爱好,出于对技术精益求精的执着来参加这个比赛的。我们挤出业余时间写出每一行代码,费尽心思地不断优化,却是在参加一个不透明的比赛,并被不负责地随便给一个评分。这是对那些学习INTEL技术并认真参加比赛的选手的不尊重和打击!另外,我建议扩大这个比赛的奖励范围,吸引更多的人来参加这个比赛,并通过奖品的形式鼓励更多的程序员(而不是某一个)学习应用TBB,从而更好地推广INTEL的先进技术。
...全文
463 24 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhangyixian525 2008-02-18
  • 打赏
  • 举报
回复
我想楼主可能对评分的规则有些误解 :)

大家有没有注意到,按照大赛总评委Clay所制定的评分规则,“执行与时间”这一项实际上只是对代码的运行速度进行评价,最快的得分最高,并利用guns30A.dat和makepulsarA.dat这两个测试文件来验证代码的准确度。

至于代码中是否存在缺陷,应该是由“精确度”这一项来评定的。我同意denghui0815的说法,“精确度”(Elegance)应该是译为“代码美感”比较合适。对于这一项,Clay给出的评分规则是这样描述的:

The Elegance score was 10 points for documentation (5 were awarded if there wasn't much more than the documentation that was given in the serial code version provided) and 25 points for following the algorithmic restrictions (grid size, use of four traversal functions, output format, etc.)

可以看到,这一项的满分只有35分,其中拥有详细的代码脚本或说明文档可获得10分,正确的计算结果、四个遍历函数的应用、结果的输出格式等项目都会算入分数。如果在算法中存在缺陷,是要在这一项中扣分的,但分值有限。

所以我的理解是,评委更注重代码的“执行与时间”,给了0-100分的空间,以求运行效率更高的作品。而对于代码的准确度,应该主要是通过使用那两个文件进行测试的,而这种评测方法是否严谨,相信是仁者见仁智者见智了。
sapzj1984 2008-02-18
  • 打赏
  • 举报
回复
顶!
denghui0815 2008-02-18
  • 打赏
  • 举报
回复
jfguo 你可以去下载一个linux下的intel编译器 然后测试
PD925测试结果比在T2050笔记本上的测试结果还要差。 :)
denghui0815 2008-02-17
  • 打赏
  • 举报
回复
zm0011的SSE指令功力的确很强 令人佩服
denghui0815 2008-02-17
  • 打赏
  • 举报
回复
parallel_test.dat 好像是恒定的数据?
yaoyanlin75 2008-02-17
  • 打赏
  • 举报
回复
我的程序也是三分法,但是和xlife的分法不一样,我是8个连续行作为一块.分3次处理
整个区域.第一次处理1,4,7...,第二次处理2,5,8,我的分法对于generator.dat这种分布的效率是最快的.

但是比赛测试的数据很稀疏,这样造成有些块没有数据,所以造成cpu利用率不充分.所以对于测试数据我的程序在双核心上
比xlife慢,但是在8核上比xlife块,我的成绩是98分.如ZM0011所述,数据分块也是很重要的.

另外,parallel_for不能重用
对于
for(i=0;i< 1000000;i++)
{
parallel_for(...)
}
这样的情况, 我测试GosperGun.dat 的时候 , 发现1/3的时间都用在parallel_for上了.
遗憾的是目前TBB还没有解决这个问题, 我看了TBB的最新动态,也许以后的版本才能解决这个问题.
jfguo 2008-02-17
  • 打赏
  • 举报
回复
谁知道评委的email,想发封信建议以后
能够公开测试平台、数据、标准,最好
还有选手的详细测试结果。
jfguo 2008-02-17
  • 打赏
  • 举报
回复
我测试了一下,结果如下。
其他几个人的代码不是太容易在Linux下编译通过,所以就没有测了。

OS:Ubuntu 7.10
Compiler: gcc 版本 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)
CPU: PD925
Memory: 2G

我的结果:
guns30A.dat 19.538470
makepulsarA.dat 22.920781
gosper.txt 25.382363

denghui0815:
guns30A.dat 22.156209
makepulsarA.dat 21.941526
gosper.txt 39.147584

我“执行与时间”一项得了43分,实在想不出评委是怎么测试的。
zm0011 2008-02-17
  • 打赏
  • 举报
回复
为了说明问题,我写了几行代码来生成一个测试数据文件,这个数据文件会使一些解决生命游戏问题的并行算法程序蜕变成单线程运行的程序,要解释清楚这个数据是怎么构思出来的,需要多费些口舌。俺写的东西都是菜鸟级的,仅供TBB粉丝共同探讨,错误难免,专家们就不必看了。

先从并行算法效率说起。要提高并行算法程序在多核机器上的效率,需要考虑的因素比较多。不过只要抓住“让每一个工作线程都疯狂地劳动”这个中心要素,我认为算法并行化就成功了一大半。具体在编码上体现为三点:

一、尽量减少互锁的原子操作,因为目前的多核CPU做互锁的原子操作前都要锁定总线,这种锁是全局的,使用这种锁也意味着要增加刷新CACHE的次数,故对追求高性能的并行算法来说是要极力回避的。

二、减少线程同步等待点,多线程程序在运行一段并行代码后,会汇集于某个线程中执行一段串行代码,然后又唤醒工作线程再执行一段并行代码……周而复始。从并行代码到串行代码的过渡中,所有的工作线程要wait同一个事件或某种同步信号,我把这个时段称为“线程同步等待点”。显然,“线程同步等待点”是影响多核CPU效能的,因为工作线程在等待时,被系统挂起,啥事也干不了。在TBB中,每执行一次parallel_for就会遇到一个“线程同步等待点”。

三、线程负载均衡,就是让每一个工作线程都有任务可做。在WINDOWS这种非实时操作系统中,线程负载均衡并不是给每个工作线程分配等量的工作任务就能实现的,而且分配等量的工作任务,有时候从算法上是根本办不到的。现实的做法是以某种“粒度”把任务划分成若干并行的子任务,然后让“快者多做”,也就是让做完一个子任务的工作线程立即随机选取下一个子任务。这里需要注意的是:“粒度”和“随机选取”。

减少锁操作是大家都容易想到的。那么“减少线程同步等待点”和“线程负载均衡”这两个关键的因素,在TBB编程中,最值得我们重点考虑的是哪一个呢?我最初想当然地认为是“线程负载均衡”,并没有实际编码验证,这是一个教训。虽然两点都很重要,但在TBB中前者更重要。TBB中的Grainsize参数是调节线程负载均衡的,这个参数的最佳选择范围比较宽。另外还有simple_partitioner和auto_partitioner类可供我们派生出专用的任务划分器。


其它要注意的一些事项有:

. 减小BODY类的大小,超过256字节的TASK对象不会从TASK POOL中分配,这对效率的影响是不可忽视的。如果TASK对象不可回避地要超过256字节,那么最好自己给BODY类实现一个浅拷贝函数。

. 根据算法的需要,可以自己指定“粒度”,TBB默认的粒度是1。

. 必要时,可以自己指定工作线程的个数,TBB默认的工作线程数与CPU个数一致。


很不幸,以上要点都是我提交答卷后才总结出来的。下面回到生命游戏问题中,继续探讨如何解决问题,然后挖掘XLife并行算法的缺陷。


我们可以先设计一个计算元胞邻居数的串行算法,然后把任务划分成若干小块,再把串行算法并行化。当加速系数公式S(p) = p / (1 + (p-1)*f)中的串行执行时间百分比f趋于零时,加速系数 S(p)趋于CPU个数p。要使串行执行时间百分比f趋于零,只要在划分子任务的算法中把“减少互锁的原子操作”、“减少线程同步等待点”、“线程负载均衡”三件事做到极致就行了。可惜鱼与熊掌往往不可兼得,有时候这三件事情是很难同时做好的,我们必须作出选择。

生命游戏问题划分子任务的算法可分为“二分法”、“三分法”、“四分法”……“九分法”等等,三分法是最直接最容易想到的,也是大部分同志做这道题时选择的划分方式,为什么恰好是三呢?这是隔离元胞的最小间距,也是实现无锁算法的前提。我选择的是“九分法”,这是一招败笔。“九分法”的优点是在于它的粒度单位是1元胞,可以通过调节Grainsize使“线程负载均衡”几乎达到完美。“九分法”的缺点是繁殖元胞一代,要执行9*4=36次parallel_for,也就是要工作线程在36个同步点上执行等待,太多了!而“三分法”繁殖元胞一代只执行3*4=12次parallel_for,它的粒度单位是5000元胞,在“线程负载均衡”上有所欠缺,但TBB对粒度并不敏感,只要在“随机选取”这点上注意一下,“三分法”应该是针对生命游戏问题综合性能比较好的一个任务划分算法。XLife的任务划分算法属于“三分法”。BUGMAN选择的是“六分法”(没细看)。还有一种“二分法”(以2ROW或2COL为整体考虑),最小间距是4,粒度单位是5000*2元胞。

不同的任务划分算法的“线程负载均衡”性是有差别的,因而对网格上元胞分布特性的敏感程度也不同。“九分法”处理各种图案的地图时性能不会有很大的落差,“二分法”在最差的情况下会比最好的情况表现相差甚远,甚至比串行算法还慢。随机生成的元胞地图有个特性,它的元胞分布及变化从整体上来看是很均匀的。各种划分任务的算法处理随机地图时,受“线程负载均衡”因素的影响最小。“三分法”在处理随机地图时,表现出很好的性能。处理均匀分布地图的最快的算法应该是“二分法”。随机地图生成器可参见http://topic.csdn.net/u/20080114/19/59a87dc3-7fe9-4d19-9c83-fe138c20a30b.html

随机生成的元胞地图大都是没有什么研究价值的,这种地图上密集的细胞群迅速大片死亡,很快就蜕变成稀疏而杂乱无章的乱石滩,甚至是不毛之地。数学家们关注的地图是,能让细胞群的繁衍发展呈现某种特定的模式,比如说,能够稳定持续地发展下去,或者逐渐消亡,或者恒定不变,或者,最有趣的是,在几个状态之内反复循环。这种“生生不息”的地图从视觉上看有很明显的分布规律,对我们的任务划分算法则是一个挑战——如何划分地图才能使线程负载均衡,特别是对于那些有规律的地图。

这里要指出的是,除了边界元胞计算错误,XLife在任务划分算法上有个问题,就像那些网络应用程序有安全漏洞一样,XLife在“线程负载均衡”处理上是有缺陷的,我们可以抓住这个小辫子不放,构造出无数的地图来验证这个缺陷,甚至可以用GUN模板构造一些更有趣的地图。下面举一个例子:

————————————————————————————————

#include "stdafx.h"
int main(int argc, char *argv[]) {
FILE *write;
write = fopen("parallel_test.dat","w");
for( int i = 2; i < 5000; i += 192 ) {
for( int j = 2; j < 4998; j += 4 ) {
fprintf(write, "%d %d\n", i, j);
fprintf(write, "%d %d\n", i, j+1);
fprintf(write, "%d %d\n", i, j+2);
}
}
fprintf(write,"%d %d\n",0,0);
fprintf(write,"%d\n",10000);
fprintf(write,"%d %d %d %d\n",190,190,200,200);
fclose(write);
}

————————————————————————————————

XLife在处理这个程序生成的数据文件“parallel_test.dat”时,表现出了巨大的性能落差,更严重的是它完全成了单线程算法。比如在2G的双核上XLife处理完“parallel_test.dat”需要10秒,在2G的四核上则需要10.01秒,在2G的8核上需要10.02秒。CPU越多,TBB线程越多,耗费在线程切换与线程同步上的时间也越多。

当然,XLife这个程序代码里面也包含了很多值得我们学习的优化技巧。我从中学习到的最有价值的方法就是“分而不合”,这种方法不仅完全避免了原子锁也实现了零拷贝,是个好办法,大家在以后的比赛中可以借鉴这种思想。由于过多的线程同步等待点及部分操作使用了原子锁,大量的内存拷贝,大BODY对象等原因,我的“九分法”程序并行度不够高(把该程序串行化后对比就可以看出这一点),虽然得益于SSE指令还是有不错的速度,但在不同的机器上表现差别较大。INTEL总说SSE是一个指令周期完成的,实际上旧型号CPU不是这样的。那个“九分法”程序还有很大的性能提升潜力,有兴趣的同志可以自己尝试修改。

最后祝贺denghui0815在本轮竞赛中提交了最佳的解决方案代码,感谢CSDN让我畅所欲言,感谢INTEL举办了这次比赛,尽管评分标准里有些非技术的因素,不是我所能把握的也不是我的兴趣所在,但我已从中学到了很多。预祝各位元宵节快乐,再见!
sapzj1984 2008-02-16
  • 打赏
  • 举报
回复
denghui0815 2008-02-16
  • 打赏
  • 举报
回复
oh 谢谢yaoyanlin75的提示 这个的确是一个缺陷
最初的源码为const int nIndex = nRow % g_nSubAry;
在优化的时候采用了Tab加速,未能考虑周全导致程序bug
uint8 g_nIndexTab[MAXROW]; (MAXROW = 5000)
应该修正为uint8 g_nIndexTab[MAXROW+2];
yaoyanlin75 2008-02-16
  • 打赏
  • 举报
回复
没有理解那敢乱说话啊
你的程序的错误就在uint8 g_nIndexTab[MAXROW]; (MAXROW = 5000)

在你的程序里有这样一段:
// 向子数组中添加Cell
__inline void XSubAryAddCell(XSubArray* pSubAry, short nRow, short nCol)
{
const int nIndex = g_nIndexTab[nRow];
......

做过这题的同志们都知道nRow的范围是 0 - 5001.
所以对nRow = 5000 和 5001 的时候, nIndex = 不确定数

现假设这时候nIndex都等与0.这样,根据你的算法,4999,5000,5001有可能被同时计算.随着迭代次数的增加,这个错误向上蔓延.

我将你的程序用数据generator.dat进行测试,分别在2核心D925 3G和8核心双Xeon上运行,并将(0-5000)(0-5000)的数据写到文件里
和串行的结果进行比较,均发现有部分结果不一样.

denghui0815 2008-02-15
  • 打赏
  • 举报
回复
谢谢yaoyanlin75的祝贺
但是 我目前还是没有发现缺陷在哪里 希望能yaoyanlin75能明确指出
我认为你没有理解我的算法 :)
我实际上等价于将5000×5000分成了3行高的小条在处理
通过3条间隔避免了并行导致的同时访问数据
yaoyanlin75 2008-02-15
  • 打赏
  • 举报
回复
其实这个比赛并不是比什么算法或者技术,只是intel的一个宣传而已.否则,判断结果的应该只有"执行和时间"这一项.

比如比赛规则里发贴子就可以得10分这个规则就很操蛋,大家在"执行和时间"这一项里花的时间最多,能提高几分要花

好大的精力,但是发一个贴子就能得10分.希望intel以后的竞赛里不会再有这种操蛋的规则,因为这个比赛不是比灌水 :)

关注"执行和时间"的同志们还是去topcoder吧.

恭喜denghui0815取得了这个问题的冠军,不过你的程序真的是有缺陷的.
killgxlin 2008-02-15
  • 打赏
  • 举报
回复
机器不同,结果确实相差很多
denghui0815 2008-02-15
  • 打赏
  • 举报
回复
估计跟测试机器有关系了 我测试的结果

Gosper.dat guns30A.dat makepulsarA.dat generator.dat
XLife 24.327204 26.933310 13.816925 32.167931
gameoflife 40.177968 47.734788 15.579041 52.858389
threadlife 48.228300 75.488400 31.629600 185.59800

最好谁有4核机器测试一下 :)
yaoyanlin75 2008-02-15
  • 打赏
  • 举报
回复
评委无奈地回答: 谁让你生在中国
zm0011 2008-02-15
  • 打赏
  • 举报
回复

关于生命游戏问题的三个程序的对比测试

bugman: threadedlife.exe (美国赛区获奖作品)
下载:http://softwarecommunity.intel.com/isn/Community/en-US/forums/thread/30248544.aspx

denghui0815: xlife.exe (中国赛区获奖作品)
下载:http://download.csdn.net/user/huanyun

zm0011: gameoflife.exe (鄙人的)
下载:http://download.csdn.net/source/344319

硬件配置: 酷睿e6600 2.88GHz(超频) DDR2 800MHz 4GB
操作系统: Windows2003中文版SP2
编译环境: Intel CPP Compiler v10.1.013 Intel IPP 5.3.1.064 tbb20_014oss
编译选项: 全部使用RELEASE方式,不改变作者设置的优化参数

测试数据共四个:

1. virus.dat,原题附带的测试数据

2. GosperGun.dat,INTLE线程赛美国论坛上找的一个数据
Gosper Gun
5 1
5 2
6 1
6 2
3 35
3 36
4 35
4 36
3 13
3 14
4 12
4 16
5 11
5 17
6 11
6 15
6 17
6 18
7 11
7 17
8 12
8 16
9 13
9 14
1 25
2 25
2 23
3 21
3 22
4 21
4 22
5 21
5 22
6 25
6 23
7 25
0 0
100000
1 1 10 40
把以上文本数据复制到一个TXT文件中,另存为GosperGun.dat即可。

3. guns30A.dat (美国赛区评测数据1)

4. makepulsarA.dat (美国赛区评测数据2)

——————————————————————————————————————————————
测试数据文件 程序名=运行时间(秒,不包括初始化及从文件中读入数据的时间)

virus.dat gameoflife=0.0111 xlife=0.0265 threadlife=0.0856

GosperGun.dat gameoflife=10.833 xlife=12.953 threadlife=24.535

guns30A.dat gameoflife=8.719 xlife=7.680 threadlife=32.145

makepulsarA.dat gameoflife=4.089 xlife=6.877 threadlife=16.906

——————————————————————————————————————————————

需要指出的是:

一、以目测屏幕上网格的局部元胞分布图形的方式,来检验程序的正确性,是不科学的不严谨的做法;

二、不同程序的运行速度与测试数据中的元胞的分布特性有很大关系,需要特别构造数据才能检验出一些并行算法设计上的瑕疵。

我将对三个程序进行简要的算法分析,总结自己在这个题目中失误的地方,及一些应用TBB的认识。随后构造几分测试数据检验那些有瑕疵的并行算法,敬请关注。





denghui0815 2008-02-14
  • 打赏
  • 举报
回复
呵呵 没关系 我也期待你的测试结果 :)
zm0011 2008-02-14
  • 打赏
  • 举报
回复
不好意思,并不是特别针对你本人,只是我手上恰好有bugman和denghui0815两份代码。

加载更多回复(4)

567

社区成员

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

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