有关对耗时很大循环进行并行化优化的探讨之三:并行线程越多运行就会越快吗?
2012-04-11 13:05
330 查看
在.net framework4.0以后,出现了并行编程的概念,使用 Parallel.For(0, N, i =>{ ... },很容易就可以实现我们自定义的并行化循环操作,在并行循环体中,还可以操作外部的变量,这个特性是很多其他语言所没有的,当然其他语言,诸如J***A之类,完全可以采用我们在第二篇所介绍的方法自己生成并行化操作。
由于.net framework使用环境的局限性,以及“庞大的身躯”,和安装时必须向微软“报到”的限制。很多开发商并不喜欢用,完全可以采用我所介绍的方式实现并行化,与微软的并行化的区别只是他的循环体是使用了lamda表达式,而我的方式是使用委托而已。再发散一下,我个人认为微软的并行化操作在并行化优化方面,多处理器利用方面会更有优势些,性能会更好。
但.Net FrameWork中对并行化并行化的最大线程数貌似并没有进行个性化限定,运行起来自然是并行任务能开多少开多少了。就并行线程数的问题,我们在某个项目中做了一个实验,具体的并行任务中既有数据库操作,又有通信操作,还包括了若干锁定资源操作,以下就是实验结果
:
从这个有趣的实验结果可以看出,某些应用下,似乎线程数越多,执行时间反而越慢了, 很多情况下,并行化的程序性能可能反而不如顺序执行,甚至会出现一些死锁等问题,这在微软的说明中提到了很多(见http://technet.microsoft.com/zh-cn/magazine/dd997392(VS.110).aspx),从这篇文章,我们可以看到,影响性能的因素主要有两大类:
1. 对共享资源的锁定问题:这个问题比较好理解,如果多个并行任务尝试操作被锁定的内存位置,那么后来的肯定要等待,对于考虑不很周到的代码,其结果就是比串行机制还慢.
2. 循环体内的对象运行机制问题
在微软的说明中,提到了“不安全的对象”并行化的问题,比如filestream, ui对象等,
另一个有趣的操作是数据库操作及通信操作, 其特征是自身就具有不可控的性能瓶颈,最好通过优化数据库的命令,如连表查询、存储过程等处理。
但对于懒人,或者不需要再精细优化的情况下,并行任务占据的线程数越少,对整体资源及其他任务的影响也越少,所以我们可以对已经封装的并行化类进行一下最大线程数的限制:
由于.net framework使用环境的局限性,以及“庞大的身躯”,和安装时必须向微软“报到”的限制。很多开发商并不喜欢用,完全可以采用我所介绍的方式实现并行化,与微软的并行化的区别只是他的循环体是使用了lamda表达式,而我的方式是使用委托而已。再发散一下,我个人认为微软的并行化操作在并行化优化方面,多处理器利用方面会更有优势些,性能会更好。
但.Net FrameWork中对并行化并行化的最大线程数貌似并没有进行个性化限定,运行起来自然是并行任务能开多少开多少了。就并行线程数的问题,我们在某个项目中做了一个实验,具体的并行任务中既有数据库操作,又有通信操作,还包括了若干锁定资源操作,以下就是实验结果
:
最大线程数 | 程序平均执行时间 |
单线程 | 31470 ms |
15线程 | 20042 ms |
5线程 | 20307 ms |
3线程 | 18745 ms |
2线程 | 18523 ms |
1. 对共享资源的锁定问题:这个问题比较好理解,如果多个并行任务尝试操作被锁定的内存位置,那么后来的肯定要等待,对于考虑不很周到的代码,其结果就是比串行机制还慢.
2. 循环体内的对象运行机制问题
在微软的说明中,提到了“不安全的对象”并行化的问题,比如filestream, ui对象等,
另一个有趣的操作是数据库操作及通信操作, 其特征是自身就具有不可控的性能瓶颈,最好通过优化数据库的命令,如连表查询、存储过程等处理。
但对于懒人,或者不需要再精细优化的情况下,并行任务占据的线程数越少,对整体资源及其他任务的影响也越少,所以我们可以对已经封装的并行化类进行一下最大线程数的限制:
class ParaLoop { .... private int _MaxThreadQty; private int _CurrentThreadQty; private ManualResetEvent ReqThreadEvent = new ManualResetEvent(false); private object _SynthreadQty = new object(); public ParaLoop(int mtq) { _MaxThreadQty = mtq; } private void ReleaseThread() //使用完后释放线程,并通知可以申请新线程 { lock (_SynthreadQty ) { _CurrentThreadQty--; ReqThreadEvent.Set(); } } ~ParaLoop() { ReqThreadEvent.Set(); } private MyParaThread RequestThread() // 申请线程,如果达到最大数则等待,直到有新的线程可用 { lock (_SynthreadQty) { if (_CurrentThreadQty < _MaxThreadQty) { _CurrentThreadQty++; return new MyParaThread(); } else { ReqThreadEvent.Reset(); } } ReqThreadEvent.WaitOne(); lock (_SynthreadQty) { _CurrentThreadQty++; return new MyParaThread(); } } public object ParaCompute(MyParaLoopTaskHandler loopTask, object[] inArg, int loopQty) { ... for (int i = 0; i < _TotalQty; i++) { MyParaThread u = RequestThread(); //由直接新建线程改为申请线程 // MyParaThread u = new MyParaThread(); } _ParaEvent.WaitOne(); return _ParaLoop_Return; } void u_EndTaskCallBack(bool taskRst, object retVal) { ... ReleaseThread(); //有返回值表示可以释放线程了 } } }
相关文章推荐
- 有关对耗时很大循环进行并行化优化的探讨之三:并行线程越多运行就会越快吗?
- 有关对耗时很大循环进行并行化优化的探讨 之一:并发搜索的处理
- 有关对耗时很大循环进行并行化优化的探讨 之一:并发搜索的处理
- 有关对耗时很大循环进行并行化优化的探讨之二:多重循环任务的并发处理
- 有关对耗时很大循环进行并行化优化的探讨之二:多重循环任务的并发处理
- 对纯for循环进行并行流和线程池优化简记
- 利用多核多线程进行程序优化
- 使用Interlocked在多线程下进行原子操作,无锁无阻塞的实现线程运行状态判断
- Win7系统删除多余的输入法进行系统运行优化
- 传统线程通信(子线程运行10次,主线程运行100次,如此循环50次)
- 子线程运行10次后,主线程运行100次,子主交叉运行。如此循环50次。
- 一个线程通信问题(sub运行10次,然后main运行100次,这样交替循环50次)
- My Eclipse运行卡,可以进行优化的几点
- 利用多核多线程进行程序优化
- 由于线程停在某个无法进行垃圾回收的点(可能是因为已对代码进行了优化),因此无法计算表达式的值的解决
- 并行开发笔记-2.基本多个线程运行带传参数
- leetcode+最长回文子串+n^2+循环便利每个元素,然后分奇偶进行探讨,找出最大的
- JSP 创建额外线程进行耗时查询
- 程序性能优化探讨(1)——周期计数器与循环展开
- (转载)Qt 的线程与事件循环——可打印threadid进行观察槽函数到底是在哪个线程里执行,学习moveToThread的使用)