最近因项目需要,需要生成有0到99999999共1亿的不重复数,于是想着直接将这些数据生成为一个文件就可以了,代码如。

private void generate(string savePath)
{
int begin = 1;
int end = 99999999;
Encoding encoding = Encoding.UTF8;
FileStream destStream = new FileStream(savePath,FileMode.Create,FileAccess.ReadWrite,FileShare.None);
for (int i = begin; i <= end; i++)
{
string code = string.Format("{0:00000000}", i);
byte[] codeBytes = encoding.GetBytes(code);
destStream.Write(codeBytes, 0, 8);
}
destStream.Close();
}

但是项目要求数据必须是随机的,所以必须要将这些数据进行随机,那么直接按顺序(比如1到99999999)生成1亿的数据显然无法满足要求,于是就想着是不是可以在生成数据时参与随机算法。代码如下

private void generate(string savePath)
{
int begin = 1;
int end = 99999999;
Encoding encoding = Encoding.UTF8;
FileStream destStream = new FileStream(savePath,FileMode.Create,FileAccess.ReadWrite,FileShare.None);
List<byte[]> codeList = new List<byte[]>();
for (int i = begin; i <= end; i++)
{
string code = string.Format("{0:00000000}", i);
byte[] codeBytes = encoding.GetBytes(code);
codeList.Add(codeBytes);
}
while (codeList.Count>0)
{
int index = generateRandomInt(0, codeList.Count);
byte[] tempCodeByte = codeList[index];
codeList.RemoveAt(index);
destStream.Write(tempCodeByte, 0, 8);
}
destStream.Close();
}
private int generateRandomInt(int min, int max)
{
Random random = new Random(Guid.NewGuid().GetHashCode());
int randomInt = random.Next(min, max);
return randomInt;
}

其中随机数算法,因为Random随机时,所用的种子一样时,产生的随机序列是一样的,所以采用GUID全球唯一标识符的哈稀码来作为种子,以使随机种子不同,从而产生不同的随机数。

但是这么做了之后,会发现程序虽然可以跑起来,但是由于数据量太大,所以采用List来存储数据会消耗大量的内存,最终电脑卡死崩溃。我的电脑配置是Win764位、4G内存、奔腾主频2.7GHZ的,但是处理起来后,内存耗达到了98%,CPU更是居高不下,最终无奈的只得重启电脑。

所以上面的代码理论上是可行的,但是当处理1亿级的数据时,就出现了瓶颈。或许会说是随机算法不太合理,不应当用List来存储。是的,我也是这么觉得,但一时又想不到好的方法。后来转念一想,现在个人电脑的主流内存一般都是2G/4G/8G,而硬盘随随便都是几百G,甚至T级,如果可以把数据放到硬盘上去随机,那是不是就可以达到目的了?

这么一想后,思路就转变成怎么将数据随机到硬盘上去了。要把数据随机到硬盘上,那只能是写文件。那又要怎么去实现随机呢?如果预先产生了10000个文件,然后再将生成的数据随机写到文件中,最后将这些文件再合并起来,那不就实现了随机吗?代码实现如下

 private void generate(string savePath)
{
int blockCount = 20000;
FileStream[] streams = new FileStream[blockCount];
string[] paths = new string[blockCount];
string tempDir = Application.StartupPath + @"\Temp";
Directory.CreateDirectory(tempDir);
for (int index = 1; index <= blockCount; index++)
{
string tempPath = tempDir + @"\" + index.ToString() + ".dat";
paths[index - 1] = tempPath;
streams[index - 1] = new FileStream(tempPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
}
int begin = 1;
int end = 99999999;
Encoding encoding = Encoding.UTF8;
List<byte[]> codeList = new List<byte[]>();
for (int i = begin; i <= end; i++)
{
int streamIndex = generateRandomInt(0, blockCount);
string code = string.Format("{0:00000000}", i);
byte[] codeBytes = encoding.GetBytes(code);
streams[streamIndex].Write(codeBytes, 0, 8);
}
mergeMultiFileStream(savePath, streams, 8);
} private void mergeMultiFileStream(string savePath,
FileStream[] streams,
int dataBlockLength = 8)
{
FileStream destStream = new FileStream(savePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None); for (int index = 0; index < streams.Length; index++)
{
int streamIndex = index;
FileStream stream = streams[streamIndex];
stream.Position = 0;
byte[] tempCodeBytes = new byte[stream.Length];
stream.Read(tempCodeBytes, 0, tempCodeBytes.Length);
destStream.Write(tempCodeBytes, 0, tempCodeBytes.Length);
stream.Close();
}
destStream.Close();
}

再运行程序后,内存大概是60%左右,而CPU大概是50%左右,大约17分钟后,生成了最终数据,但是用UE打开后一看,发现数据自前后后,整体还是呈现自小而大块状分布。这是怎么回事呢?仔细一想,恍然大悟,在程序生成数据时,是按顺序生成的,虽然将这些数据写入文件时,数据是随机写入的,但是整体上是小的数据写在了前面,大的数据写在了后面,最终合并时是将文件一个一个合并的,所以数据整体上还是自小而大的,只是会以一块一块的形式呈现。那有没有什么办法可以改进呢?

0到99999999个数据,每个数据必须要8位,以8个字节存储,理论上是有786M左右,分成了10000份后,就有78.8K左右,这样的数据如果放到内存里面,然后再随机,显然内存是可以承受的,于是在合并文件时,又将每个文件读到的数据增加了一次随机处理。将mergeMultiFileStream函数的代码修改如下:

private void mergeMultiFileStream(string savePath,
FileStream[] streams,
int dataBlockLength = 8)
{
FileStream destStream = new FileStream(savePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None); for (int index = 0; index < streams.Length; index++)
{
int streamIndex = index;
FileStream stream = streams[streamIndex];
stream.Position = 0;
byte[] tempCodeBytes = new byte[stream.Length];
stream.Read(tempCodeBytes, 0, tempCodeBytes.Length);
tempCodeBytes = randomData(tempCodeBytes, dataBlockLength);
destStream.Write(tempCodeBytes, 0, tempCodeBytes.Length);
stream.Close();
}
destStream.Close();
} private byte[] randomData(byte[] sourceData, int randomDataBlockLength)
{
List<byte[]> tempData = new List<byte[]>();
for (int index = 0; index < sourceData.Length; index += randomDataBlockLength)
{
int dataLength = randomDataBlockLength;
if (sourceData.Length - index < randomDataBlockLength)
{
dataLength = sourceData.Length - index;
}
byte[] tempDataBlock = new byte[dataLength];
Array.Copy(sourceData, index, tempDataBlock, 0, dataLength);
tempData.Add(tempDataBlock);
}
byte[] newData = new byte[sourceData.Length];
int newDataCopyBeginIndex = 0;
while (tempData.Count > 0)
{
int index = generateRandomInt(0, tempData.Count);
byte[] tempDataBlock = tempData[index];
Array.Copy(tempDataBlock, 0, newData, newDataCopyBeginIndex, tempDataBlock.Length);
newDataCopyBeginIndex += tempDataBlock.Length;
tempData.RemoveAt(index);
}
tempData.Clear();
return newData;
}

这里要注意的是,为了确保randomData函数中所用的List能释放,增加了tempData.Clear()的处理,以清空List,从而避免因垃圾回收不够及时,而导致内存占用过多的情况。

这样一样,程序再次跑了起来,得到的数据已经随机了。如果还要进一步随机,可以将生成后的文件,再次读取分成多个文件(如10000),再随机处理合并。

达到了目的之后,就想着怎么去优化,能不能在更短的时间里去处理完数据,于是想到了多线程。但是当我将多线程加进去处理后,发现并不能达到太理想的效果,因为CPU和内存的占用又一次居高不下,电脑又一次卡死。

这是怎么回事?

分析了一番之后,得出这样的结论,一般情况下,若是采用多线程处理,确实可以加快速度。但是由于这里的数据量太大,需要开多一点的线程来处理数据,才能提升速度,但是当分成多个线程处理时,虽然单位时间内多个线程可以并行处理数据,但这样一样,每个线程都会消耗内存,都要CPU处理,所以线程一多,内存消耗就会变大,CPU的占用也就会变高。

可能是我当时开了太多线程导致了这样的结果,或许开少一占的线程,比如5个或者10个,但这样一来,又需要协调好文件数量的分配,让每个线程处理若干个文件,而为了协调好这些,需要有自增操作(文件流数组索引是逐个递增的),而在多线程中的自增操作,必须采用原子性的增加(Interlock.Increacement),否则会出现自增时的竞争,从而导到意外的结果。比如A线程要用某个流时,因流数据索引的错位,导致B线程也使用到了同一个流,而B线程又快于A线程处理完数据,结果 流被关闭,A线程无法操作。即便随开了流的关闭,但A线程和B线程引用了同样的流,这样的数据就会出现重复,无法满足项目要求。

所以最终,为了确保数据的准确性,没有采用多线程来处理。

从这里也看出另一点,虽然使用内存来处理数据比IO处理数据时更快,但是由于内存大小的局限,当数据量达到一定量级时,内存就会捉襟见肘,这时不可避免的要使用磁盘来转存数据,以分担内存的消耗。所以在亿级数据时,如果全部采用内存来处理,其性能并不会高于IO操作的性能。

转载请注明出处http://blog.csdn.net/xxdddail/article/details/12645957

亿级数据时,内存性能低于IO性能的更多相关文章

  1. 横瓜先生如何用MDB和XLS等低性能数据库来处理千亿级数据量。

    横瓜先生如何用MDB和XLS等低性能数据库来处理千亿级数据量. 横瓜先生曾经用ACCESS做数据库,开发出高性能CMS来处理过TB级的文本数据量,任何请求都可以在10MS内完成,基本就是硬盘延迟的时间 ...

  2. MySQL处理达到百万级数据时,如何优化?

    1.两种查询引擎查询速度(myIsam 引擎 ) InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行. ...

  3. mysql如何处理亿级数据,第一个阶段——优化SQL语句

    1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 2.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉 ...

  4. 使用Python Pandas处理亿级数据

    在数据分析领域,最热门的莫过于Python和R语言,此前有一篇文章<别老扯什么Hadoop了,你的数据根本不够大>指出:只有在超过5TB数据量的规模下,Hadoop才是一个合理的技术选择. ...

  5. [NewLife.XCode]百亿级性能

    NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode. 整个系列教程会大量结合示例代码和 ...

  6. 亿级用户下的新浪微博平台架构 前端机(提供 API 接口服务),队列机(处理上行业务逻辑,主要是数据写入),存储(mc、mysql、mcq、redis 、HBase等)

    https://mp.weixin.qq.com/s/f319mm6QsetwxntvSXpKxg 亿级用户下的新浪微博平台架构 炼数成金前沿推荐 2014-12-04 序言 新浪微博在2014年3月 ...

  7. 【好书摘要】性能优化中CPU、内存、磁盘IO、网络性能的依赖

    系统优化是一项复杂.繁琐.长期的工作,优化前需要监测.采集.测试.评估,优化后也需要测试.采集.评估.监测,而且是一个长期和持续的过程,不 是说现在优化了,测试了,以后就可以一劳永逸了,也不是说书本上 ...

  8. Linux按照CPU、内存、磁盘IO、网络性能监测

      系统优化是一项复杂.繁琐.长期的工作,优化前需要监测.采集.测试.评估,优化后也需要测试.采集.评估.监测,而且是一个长期和持续的过程,不 是说现在优化了,测试了,以后就可以一劳永逸了,也不是说书 ...

  9. 百亿级别数据量,又需要秒级响应的案例,需要什么系统支持呢?下面介绍下大数据实时分析工具Yonghong Z-Suite

    Yonghong Z-Suite 除了提供优秀的前端BI工具之外,Yonghong Z-Suite让用户可以选购分布式数据集市来支持实时大数据分析. 对于这种百亿级的大数据案例,Yonghong Z- ...

随机推荐

  1. Bootstrap 貌似不错,先做一下记录

    Bootstrap 简洁.直观.强悍的前端开发框架,让web开发更迅速.简单. http://www.bootcss.com/

  2. 李洪强漫谈iOS开发[C语言-036]-C语言前四天学习小结

  3. QAbstractItemView为截断的项显示ToolTip(在eventFilter函数里覆盖QEvent::ToolTip事件)

    在Qt中想要为QAbstractItemView中长度不够而使得内容被截断的项显示ToolTip,Qt官网有一篇文章介绍使用事件过滤器来显示太长的项,但是没有涵盖图标的情况.显示列头项太长的情况等等, ...

  4. rebuild过程

    /** The class defining a handle to an Innodb table */ class ha_innobase: public handler { row_prebui ...

  5. BZOJ_1821_[JSOI2010]_部落划分_(贪心,并查集)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1821 给出n个点的坐标,将n个点划分成k个部分,使得每个部分之间最小的距离最大. 分析 每两个 ...

  6. wpa_supplicant使用笔记

    还在搞8634,看不到头了..唉.wireless_tools的缺点是不支持WPA认证,所以有必要把wpa_supplicant也移植过来.无线 网卡是基于zydas芯片的,正好可以在网上搜到wpa_ ...

  7. 从修复 testerhome(rubychina)网站的一个 bug 学习 ruby&rails on ruby

    前言 testerhome: http://testerhome.com/topics/1480 对于一个差点脱离前沿技术人,想要学习ruby,就意味着要放弃熟悉的操作系统windows,熟悉的ide ...

  8. 使用Flashbuilder/Flashbuilder-plugins搭建Flex工程每日构建(自动化构建)的方法

    前段时间研究flex工程自动编译的时候,遇到了阻碍,就放下了,直到今天每日构建的问题又一次给项目组带来了麻烦,于是我彻底愤怒了. 最后,我的怒火没有白费,写出来以发泄情绪. [基本原理]: adobe ...

  9. C#索引器的作用及使用

    1. 作用: 可以使得类和实例能够像数组那样使用一样,又称为带参属性 2. 区分 (1)索引器与数组的比较: 索引器的索引值不受类型限制.用来访问数组的索引值一定是整数,而索引器可以是其他类型的索引值 ...

  10. 从打车软件你能想到多少?盈利模式?商机?大数据?移动互联网蛋糕?生活方式改变withApp?

    物联网.云服务.大数据.. 淘宝.12306.卡通...一个产品改变一次生活方式. 打车app,无疑是改变生活方式的又一个产品.从打车软件,你能看到什么? 个人认为,打车软件值得各种投资方斥资,最重要 ...