.net TPL并行计算内存不足问题?请教高手
使用.net 的tpl并行计算,主要是从Excel表中读取21万条数据,对满足要求的赋值到Hashtable中,采用并行计算,为什么每次读到4万多条时,提示“没有足够的内存执行该程序”?而在串行计算时,就不会存在这个问题,串行计算大概用1个小时完成。本人笔记本电脑T400双核CPU,内存3G。代码如下,请教高手指教是哪里出了问题://ws为Excel表单
int sumlength = 210000;
Hashtable table1 = new Hashtable();
Hashtable table2 = new Hashtable();
Parallel.For(1,sumlength, i=>
{
//对每一行,读取Excel表单ws第2列、第3列的数据
string str1 = ((Excel.Range)ws.Cells[i, 2]).Text.ToString().Trim();
string str2 = ((Excel.Range)ws.Cells[i, 3]).Text.ToString().Trim();
//如果满足规则,哈希表中增加数据
if ((!String.IsNullOrEmpty(str1)) && str1.Contains("在"))
{
table1.Add(i.ToString(), "1");
}
if ((!String.IsNullOrEmpty(str2)) && (!String.Equals(str2, "无")))
{
table2.Add(i.ToString(), "1");
}
}
); --------------------编程问答-------------------- 其实就算是你把任务分为10个线程,你这个程序不一定会比单线程快。
而你分为21万个线程?哪有这样分的?也不能将Parallel滥用到这种“颗粒度过细令人发指”的程度吧!
你可以分为11个Parallel任务,每一个负责处理2万条记录,这也许还行。 --------------------编程问答-------------------- 一个线程不管运不运行至少分配1兆内存,贪婪的并发内存耗尽必然的 --------------------编程问答-------------------- Parallel.For(1,10, x=>
{
for (int j = 0; j < sumlength / 10; j++)
{
int i = j * 10 + x;
//对每一行,读取Excel表单ws第2列、第3列的数据
string str1 = ((Excel.Range)ws.Cells[i, 2]).Text.ToString().Trim();
string str2 = ((Excel.Range)ws.Cells[i, 3]).Text.ToString().Trim();
//如果满足规则,哈希表中增加数据
if ((!String.IsNullOrEmpty(str1)) && str1.Contains("在"))
{
table1.Add(i.ToString(), "1");
}
if ((!String.IsNullOrEmpty(str2)) && (!String.Equals(str2, "无")))
{
table2.Add(i.ToString(), "1");
}
}
}
--------------------编程问答-------------------- int i = sumlength * x / 10 + j;
这样可能快一些。但是就你的代码来说,不好说。Excel的调用可能会被上锁。
--------------------编程问答-------------------- .net tpl 不是自己在后台自动将数据按照tpl自带的算法分块,并行计算么?还需要人工分好了让.net去执行么?按照我这个例子,并行计算21万条数据,tpl难道不会按照自己的算法在后台自动分成n(2个或10个等等)个线程去执行?请大虾指教!
--------------------编程问答-------------------- 它是自己控制规模的,但是那种控制应该是非常是有限的。比如说你盖10个养猪场每个养猪场最多养200头猪,显然比你盖2000个养猪场的初始化资源要少。
当然具体的要你自己测试。这里很难确定。但是毫无疑问地,并发任务划分“过细”以至于一下子就创建21万个任务这恐怕连微软的那个项目组设计这个TP框架时也没有想到要支持你这种需求,他们一定没有把这个作为正规的需求、测试用例来对待过。
另外要注意,在并发任务中使用table1、table2,必要时要考虑加锁。甚至你要考虑使用HashTable是否正确。 --------------------编程问答-------------------- 是不是 执行Parallel.ForEach时,TPL会自动将整个集合分成多个不相交的子集,
而在执行Parallel.For时,TPL不会这样做,需要人工提前划分好? --------------------编程问答-------------------- 谢谢大虾!
当时也考虑到线程之间Hashtable存储时冲突的问题,但是加锁的话,计算速度上不去了,除了哈希表,还有其他好的数据存储方式么?或者为每个任务建1个独立的哈希表,最后再合并到一起这种方式?
--------------------编程问答-------------------- 基础知识不扎实
自定义数据分区
parallel.foreach (Partitioner.Create(1, 任务数量n , 任务数n/处理器数+1),...)
最大并行数, 也可以指定, parallelOption.MaxDegreeOfPara***
任务调度器,你这个就不需要了
你的多线程的算法和数据结构, 也不行.
对处理器缓存Cache也极不友好.慢5倍,都是可能的. 读Excel几行,又去判断字符串,hash, 大量的缓存/指令分支预测失效.
[你的代码, 从CPU的缓存,到硬盘的缓存, 所有的一切Cache, 都搞得一塌糊涂.]
应该 AAA读取数据. BBB处理数据. 并行的生产者,消费者
还有,这四处充满了 代价很高的同步,也是错误的.
另外,复杂计算,参照MapReduce 的思路
map = xxx.asparallel().ToLookUp
reduce = map.asParallel.xxx.xxx....
..
还有, toString, 又 trim, 大量的string对象生灭,性能很差的.
------------------------------
就你的例子
很明显, A ==> data ==>B , 有很多IProducerConsumerCollection的可选. 当然,针对这个例子,有更高性能的,自己写的版本.
A: 多线程,预先分块, 每个线程块内线性顺序读取 excel 文件.
Enqueue ==> 并行无锁的 ConcurrentQueue或者其它的
(为什么,读取硬盘IO要多线程? 现代机械盘支持NCQ以及SSD,在多线程队列深度更大的情况下,性能更高.有大量数据测试. 关键词: 队列深度 SSD 评测)
B:TryDequeue, 各线程,维护自己的 数据,
C: 归并B产生的数据 --------------------编程问答-------------------- 并行第一课: Amdahl 定律. 串行代码的比例. (盲目堆硬件, 就是给你一万个处理器,垃圾代码的性能也和单处理器性能一样)
充满了高代价的同步, 串行代码. 还有 伪共享,AB乒乓的问题.
intel IA32 优化书, 有很多例子. 因为垃圾的数据结构, 造成10倍以上的性能差距
------------
你的 内存不足, 原因是因为 String . 21万数据, 可能临时的string有三四十万.或者更多 --------------------编程问答-------------------- “这四处充满了 代价很高的同步”,这几处到底哪几处,代价如何高?请说清楚。
另外,如果你设计这段程序,你大概怎样做,请清楚的列出1、2、3、4?
上面你说的太笼络了。。。。。
--------------------编程问答--------------------
这个, 是泛泛而谈, 指的是常见的 并行编程的错误. 可能你的代码,没有这个错误,没仔细看
你可以参照 MSDN , ConcurrentQueue ,或者其它并行容器里面的, 例子.
你的题目,可以典型的 生产者,消费者. 并且,通过类似mapReduce的思路, 把同步降到很低
最好呢, 还是系统性地学习, 按照科班的培养计划, 本科,硕士,博士等这些的培养套路.
--------------------编程问答-------------------- 我是虚心请教的,请大虾针对这个案例,说几处需要明确改进的地方,先谢过了。
本人不是计算机专业,业余喜欢编程,请大虾不吝赐教!
--------------------编程问答-------------------- 我在 9# 后面的 A..B..C, 已经说了,基本的流程.
S01: 可以单线程,或者多线程, 简单读取excel ,以行或者单元格为单位, 存储到一个并行无锁队列queue中 == >作为生产者
S02: 可以单线程,或者多线程, 从 队列queue中取出一个需要处理的数据.
---------这是最基本的. S01, 和 S02 分开了.
以后,可能不要excel,换成csv格式的文件, 都可以,只需换写S01
高级一点, S02 可以使用类似mapReduce的思路.可以用一个"调度器",根据实际的数据的特性把不同数据分布在不同的线程, 各个线程,独立处理数据,无需同步. 全部完成后,再归并
还可以进一步重构, 玩出很多花样. --------------------编程问答-------------------- 简单的程序非要写别人看不懂的代码。无聊。
补充:.NET技术 , C#