当前位置:编程学习 > C#/ASP.NET >>

.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个等等)个线程去执行?请大虾指教!

引用 1 楼 sp1234 的回复:
其实就算是你把任务分为10个线程,你这个程序不一定会比单线程快。

而你分为21万个线程?哪有这样分的?也不能将Parallel滥用到这种“颗粒度过细令人发指”的程度吧!

你可以分为11个Parallel任务,每一个负责处理2万条记录,这也许还行。
--------------------编程问答-------------------- 它是自己控制规模的,但是那种控制应该是非常是有限的。比如说你盖10个养猪场每个养猪场最多养200头猪,显然比你盖2000个养猪场的初始化资源要少。

当然具体的要你自己测试。这里很难确定。但是毫无疑问地,并发任务划分“过细”以至于一下子就创建21万个任务这恐怕连微软的那个项目组设计这个TP框架时也没有想到要支持你这种需求,他们一定没有把这个作为正规的需求、测试用例来对待过。

另外要注意,在并发任务中使用table1、table2,必要时要考虑加锁。甚至你要考虑使用HashTable是否正确。 --------------------编程问答-------------------- 是不是 执行Parallel.ForEach时,TPL会自动将整个集合分成多个不相交的子集,
而在执行Parallel.For时,TPL不会这样做,需要人工提前划分好? --------------------编程问答-------------------- 谢谢大虾!
当时也考虑到线程之间Hashtable存储时冲突的问题,但是加锁的话,计算速度上不去了,除了哈希表,还有其他好的数据存储方式么?或者为每个任务建1个独立的哈希表,最后再合并到一起这种方式?

引用 6 楼 sp1234 的回复:
它是自己控制规模的,但是那种控制应该是非常是有限的。比如说你盖10个养猪场每个养猪场最多养200头猪,显然比你盖2000个养猪场的初始化资源要少。

当然具体的要你自己测试。这里很难确定。但是毫无疑问地,并发任务划分“过细”以至于一下子就创建21万个任务这恐怕连微软的那个项目组设计这个TP框架时也没有想到要支持你这种需求,他们一定没有把这个作为正规的需求、测试用例来对待过。

另外要注意,……
--------------------编程问答-------------------- 基础知识不扎实

自定义数据分区
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?
上面你说的太笼络了。。。。。

引用 9 楼 CandPointer 的回复:
基础知识不扎实

自定义数据分区
parallel.foreach (Partitioner.Create(1, 任务数量n , 任务数n/处理器数+1),...)
最大并行数, 也可以指定, parallelOption.MaxDegreeOfPara***
任务调度器,你这个就不需要了


你的多线程的算法和数据结构, 也不行.
对处理器缓存Cache也极不友好.慢5倍,都是……
--------------------编程问答--------------------
引用 11 楼 rainbowguy 的回复:
“这四处充满了 代价很高的同步”,这几处到底哪几处,代价如何高?请说清楚。……


这个, 是泛泛而谈, 指的是常见的 并行编程的错误. 可能你的代码,没有这个错误,没仔细看

你可以参照 MSDN ,  ConcurrentQueue ,或者其它并行容器里面的, 例子.

你的题目,可以典型的 生产者,消费者. 并且,通过类似mapReduce的思路, 把同步降到很低

最好呢, 还是系统性地学习, 按照科班的培养计划, 本科,硕士,博士等这些的培养套路.
--------------------编程问答-------------------- 我是虚心请教的,请大虾针对这个案例,说几处需要明确改进的地方,先谢过了。
本人不是计算机专业,业余喜欢编程,请大虾不吝赐教!

引用 12 楼 CandPointer 的回复:
引用 11 楼 rainbowguy 的回复:
“这四处充满了 代价很高的同步”,这几处到底哪几处,代价如何高?请说清楚。……

这个, 是泛泛而谈, 指的是常见的 并行编程的错误. 可能你的代码,没有这个错误,没仔细看

你可以参照 MSDN ,  ConcurrentQueue ,或者其它并行容器里面的, 例子.

你的题目,可以典型的 生产者,消费者. 并且,通过类似mapRe……


引用 12 楼 CandPointer 的回复:
引用 11 楼 rainbowguy 的回复:
“这四处充满了 代价很高的同步”,这几处到底哪几处,代价如何高?请说清楚。……

这个, 是泛泛而谈, 指的是常见的 并行编程的错误. 可能你的代码,没有这个错误,没仔细看

你可以参照 MSDN ,  ConcurrentQueue ,或者其它并行容器里面的, 例子.

你的题目,可以典型的 生产者,消费者. 并且,通过类似mapRe……
--------------------编程问答-------------------- 我在 9# 后面的 A..B..C, 已经说了,基本的流程.

S01: 可以单线程,或者多线程, 简单读取excel ,以行或者单元格为单位, 存储到一个并行无锁队列queue中 == >作为生产者

S02: 可以单线程,或者多线程, 从 队列queue中取出一个需要处理的数据.

---------这是最基本的. S01, 和 S02 分开了. 
以后,可能不要excel,换成csv格式的文件, 都可以,只需换写S01

高级一点, S02 可以使用类似mapReduce的思路.可以用一个"调度器",根据实际的数据的特性把不同数据分布在不同的线程, 各个线程,独立处理数据,无需同步. 全部完成后,再归并
还可以进一步重构, 玩出很多花样. --------------------编程问答-------------------- 简单的程序非要写别人看不懂的代码。无聊。
补充:.NET技术 ,  C#
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,