西西河

主题:【求助】:请教信息技术方面的大牛有关多线程的问题 -- newtime

共:💬51 🌺45
全看树展主题 · 分页首页 上页
/ 4
下页 末页
家园 极大可能是共享变量访问的问题

如果你的这个程序是C++的,把ramdon_number去掉以后应该没有任何问题,多线程在多核CPU上肯定快。但对Fortran程序,按我对这个语言的粗浅理解,Thread_Proc0的所有局部变量在两个线程都是共享的(同时说明:你的运算可能因此无法获得你预期的结果)。如果是并行版的编译器,那么访问这些变量的所有操作都要自动加锁,如此的话多线程程序将显著慢于单线程程序。

一个测试方法是把Thread_Proc0复制一份并改名Thread_Proc1,然后两个线程分别调用两个运算函数。如果这个方式下效率显著提高,则说明是Fortran语言的问题。否则要另找原因。

家园 关于并行计算的一些基本概念的回忆

两年没碰并行计算了,我当年还是下过一些功夫,靠这个混到学位了。

1、线程、进程的区别

简单的说,就是线程间共享内存地址空间,可以直接进行数据交换,而进程不共享内存地址空间,需通过进程间通信进行数据交换。

咋一看,多线程模式数据交换更方便、直接,但事实上,对于计算密集型的并行计算而言,多进程效率远高于多线程。其根本原因是缓存的影响。

多级缓存结构是冯诺依曼以来,计算机体系结构的最大进步,解决了运算器的高速要求和存储器的大容量要求的矛盾,通过小而快的寄存器、一级缓存、二级缓冲,到大而慢的内存、外存的分级处理,大大提高了运算效率。一般而言,计算密集型程序读写内存的指令数在4%左右,内存延迟一般在100时钟周期以上,计算机体系架构和编译器优化效果的关键,就在对读写内存的指令数的处理上。

多线程程序的最大问题,就是将各线程间缓存的处理交给了操作系统,用线程池等方法进行分配,而操作系统并不清楚程序员的实际意图,所以效率一般不高。而多进程程序各进程之间不会相互干扰,需要进行信息传递的数据由程序员显式指定,亲自设计通信方法,有利于针对缓存的优化。

究其根本,多线程程序的起源,是在单个CPU上满足多个用户的非计算密集型需求,只要延迟小于几秒,用户的体验不会受到影响,采用多线程编程,可以很容易的写出满足多用户业务操作的商业应用系统。而对于计算密集型任务,常用高效的处理方法是批处理。

简而言之,多线程程序不适合采用目前的多级缓存、多核处理器体系架构下设计计算密集型程序。事实上,OpenMP这套东西效果也很差,因为其前身MPP机器的缓存结构,与MPI所针对的集群结构相比,也是非常简单的,目前的多核机器的缓存结构,更适合于MPI。

家园 【求助】

对于计算密集型问题,在多核上多进程才是更加合适的吗?按照您的指导,我用多进程试试。

家园 【讨论】

好,试一下。我也有这种怀疑。但是已经加入affinity的命令。也许没有什么作用吧

家园

嗯。我试一下mpi

家园 你还没理解

一人能扛一袋100斤的大米,你让他扛2袋每袋60斤的大米,你说他能扛动吗?

科学计算本质上与听音乐、放视频等是不同的,通常一个进程、线程就能把CPU占满,根本不用多进程、多线程提高单CPU上的效率。相反,在单CPU上多线程、多进程反而会降低效率。

autoeagle说的多进程,通常是指多台机器上运行的,最差也是一台机器上多路CPU上运行的。

至于多核CPU,目前对科学计算没戏,因为它没解决内存访问的瓶颈。

同理,我非常怀疑CUDA架构的有效性,因为它好象也没有解决内存访问的瓶颈。

家园 多核CPU对科学计算支持的很好

目前的多核机,其巨大的多级缓存是独立的,所以基本解决了内存访问瓶颈,但前提是各计算区域的相对独立性。一般而言,运行多进程程序,多核效果比多路好,多路效果比多机好,毕竟内部通信效率要比外部高。 你所说的,应该是超线程技术在科学计算中的应用不良,这就是因为超线程技术依靠的是一条运算流水线上的两套处理单元,但没有设立独立缓存和寄存器的结果。不过SSE技术用好了,可以明显提高科学计算效率。 GPU计算的结构比较接近于当年的MPP,大规模并行机,而且缓存结构更加简单,只有对那些进程通信非常少的大规模计算程序,如地震资料解析,多路流媒体处理,视频图像处理等方面效果好,其实其本来也是做事情的。

家园 请教:老兄看这个问题如何优化

我通常解的二维问题:

网格是1024*1024,这样是1M个节点,一个节点要5个变量,一个变量8个字节(double)。这样一个稀疏矩阵存储至少要40M。解这个线性方程组,就是不断读这个40M的数组的过程。但问题是Cache不可能放下这个数组。

所以,在一个CPU或双路CPU上,我认为是没法解决的。

老兄有何高见?

家园 两种处理方法

对于超大规模稀疏矩阵的并行计算,可以只存储非零元素,用链表或者哈希结构压缩存储。缺点是会增加每次有效操作的读取次数,但对于超大规模稀疏矩阵,绝大部分元素都是0,这种情况下,用链表遍历的读取次数要远小于数组遍历。

另一方面,缓存与内存存在特殊的映射关系,缓存中的内容对应内存的某个区域,只要你的读取操作都集中在这个区域内,缓存更新频率低,影响也很小,忌讳的是在读取过程中坐标乱跳,这样缓存需要经常更新。你在编程过程中,可以将数组遍历过程与缓存大小相结合,采用中间变量和特殊算法,提高效率。

当年缓存只有64K的时代,内存才几兆的时代,这种小技法是很多的,只是现在动辄8M缓存,几十G内存,大家反而少用了这些优化技术了。

家园 有时间的话,推荐看看《计算机程序设计艺术》这套巨著

缓存装不下真是小问题了。当年科学家们遇到的问题是内存装不下,如何改进算法,减少读硬盘的问题。更早之前,是硬盘都装不下,如何改进算法,减少读磁带的问题。这些问题的解答,都在那套书里。

家园 问题已经解决

要感谢各位热心筒子的帮助,尤其是autoeagle,给出了很多的解释和信息。:)

这个问题已经解决。总结一下

1 random_number函数不适合用多线程计算

2 需要disable bios 上的 hthread 和 turbo mode

现在四线程所费的时间已经非常接近于一线程的四分之一。

我用的是openmp。

家园 祝贺一下

另外提醒,你的问题基本没有通信,规模也小,用OpenMP还行,复杂点的还是要用MPI。当年我用MPI+openMP+SSE2共同优化,同样的机器效率提升180%,非常自豪喔

家园 多线程这玩意纯粹是微软推广出来的

看起来很美,其实还不如多进程

家园 本质是一样的

都是利用更快更贵的存储器来存放本来存不下的数据,只不过技术让整个缓存架构变多了。

intel CPU里面的超线程hyperthreading,其实是让一组计算单元被多组context registers使用,所以在两个thread没有共同的硬件资源的时候,是可以提高执行速度的;但是如果两个thread竞争同一个计算资源,那么就要等待,同时还增加了context switch的时间,必然是更慢的。

另外鸡蛋里面挑根骨头

计算机体系架构和编译器优化效果的关键,就在对读写内存的指令数的处理上。

老叫花看来,这个优化的基本原则,是找瓶颈,用最少的努力去获得最大的收益。编译优化最早是优化循环这个大瓶颈,循环弄的差不多了,访存就变成最大的瓶颈了。访存也有很多种优化方法,比如你说的对指令数的优化,或者更有效一点的:prefectch.

另:谢宝

恭喜:你意外获得【通宝】一枚

鲜花已经成功送出。

此次送花为【有效送花赞扬,涨乐善、声望】

家园 我还遇到过jvm把内存吃光的事情

给了-X都不行,当时是要验证自己写的算法的有效性。后来的解决办法是:不用jvm提供的基本对象,因为java.lang.Object在一般的实现里面,每个对象有8个byte的overhead,只能自己用大数组去存每个实例的一个属性,这样每个数组有这个overhead,而不是每个对象都浪费 8 bytes。

除了autoeagle的建议以外,我建议你去看看你的程序是否有某种访存模式。我隐约记得解方程组的时候,访存通常是逐行/逐列,最多是按对角线来。这样的话,你可以安排数据在内存里面的排列顺序,CPU会按cache line来读取数据,如果你的数据安排合适,每个cache line的第一个数据读过以后,后面的数据也在cache里面了。

或者你自己去添一两句汇编,prefetch一下后面一个循环要用的数据。

全看树展主题 · 分页首页 上页
/ 4
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河