含有5亿个整数的大文件,如果排序?

给你1个文件bigdata,大小4663M,5亿个数,文件中的数据随机,如下一行一个整数:

61963023557681612158020393452095006174677379343122016371712330287901712966901...7005375

现在要对这个文件进行排序,怎么搞?

内部排序

先尝试内排,选2种排序方式:

 
  1. privatefinalint cutoff = 8;

  2.  

  3. public void perform(Comparable[] a) {

  4. perform(a,0,a.length - 1);

  5. }

  6.  

  7. private int median3(Comparable[] a,int x,int y,int z) {

  8. if(lessThan(a[x],a[y])) {

  9. if(lessThan(a[y],a[z])) {

  10. return y;

  11. }

  12. elseif(lessThan(a[x],a[z])) {

  13. return z;

  14. }else {

  15. return x;

  16. }

  17. }else {

  18. if(lessThan(a[z],a[y])){

  19. return y;

  20. }elseif(lessThan(a[z],a[x])) {

  21. return z;

  22. }else {

  23. return x;

  24. }

  25. }

  26. }

  27.  

  28. private void perform(Comparable[] a,int low,int high) {

  29. int n = high - low + 1;

  30. //当序列非常小,用插入排序

  31. if(n <= cutoff) {

  32. InsertionSort insertionSort = SortFactory.createInsertionSort();

  33. insertionSort.perform(a,low,high);

  34. //当序列中小时,使用median3

  35. }elseif(n <= 100) {

  36. int m = median3(a,low,low + (n >>> 1),high);

  37. exchange(a,m,low);

  38. //当序列比较大时,使用ninther

  39. }else {

  40. int gap = n >>> 3;

  41. int m = low + (n >>> 1);

  42. int m1 = median3(a,low,low + gap,low + (gap << 1));

  43. int m2 = median3(a,m - gap,m,m + gap);

  44. int m3 = median3(a,high - (gap << 1),high - gap,high);

  45. int ninther = median3(a,m1,m2,m3);

  46. exchange(a,ninther,low);

  47. }

  48.  

  49. if(high <= low)

  50. return;

  51. //lessThan

  52. int lt = low;

  53. //greaterThan

  54. int gt = high;

  55. //中心点

  56. Comparablepivot = a[low];

  57. int i = low + 1;

  58.  

  59. /*

  60. * 不变式:

  61. * a[low..lt-1] 小于pivot -> 前部(first)

  62. * a[lt..i-1] 等于 pivot -> 中部(middle)

  63. * a[gt+1..n-1] 大于 pivot -> 后部(final)

  64. *

  65. * a[i..gt] 待考察区域

  66. */

  67.  

  68. while (i <= gt) {

  69. if(lessThan(a[i],pivot)) {

  70. //i-> ,lt ->

  71. exchange(a,lt++,i++);

  72. }elseif(lessThan(pivot,a[i])) {

  73. exchange(a,i,gt--);

  74. }else{

  75. i++;

  76. }

  77. }

  78.  

  79. // a[low..lt-1] < v = a[lt..gt] < a[gt+1..high].

  80. perform(a,low,lt - 1);

  81. perform(a,gt + 1,high);

  82. }

归并排序:

 
  1. /**

  2. * 小于等于这个值的时候,交给插入排序

  3. */

  4. privatefinalint cutoff = 8;

  5.  

  6. /**

  7. * 对给定的元素序列进行排序

  8. *

  9. * @param a 给定元素序列

  10. */

  11. @Override

  12. public void perform(Comparable[] a) {

  13. Comparable[] b = a.clone();

  14. perform(b, a, 0, a.length - 1);

  15. }

  16.  

  17. private void perform(Comparable[] src,Comparable[] dest,int low,int high) {

  18. if(low >= high)

  19. return;

  20.  

  21. //小于等于cutoff的时候,交给插入排序

  22. if(high - low <= cutoff) {

  23. SortFactory.createInsertionSort().perform(dest,low,high);

  24. return;

  25. }

  26.  

  27. int mid = low + ((high - low) >>> 1);

  28. perform(dest,src,low,mid);

  29. perform(dest,src,mid + 1,high);

  30.  

  31. //考虑局部有序 src[mid] <= src[mid+1]

  32. if(lessThanOrEqual(src[mid],src[mid+1])) {

  33. System.arraycopy(src,low,dest,low,high - low + 1);

  34. }

  35.  

  36. //src[low .. mid] + src[mid+1 .. high] -> dest[low .. high]

  37. merge(src,dest,low,mid,high);

  38. }

  39.  

  40. private void merge(Comparable[] src,Comparable[] dest,int low,int mid,int high) {

  41.  

  42. for(int i = low,v = low,w = mid + 1; i <= high; i++) {

  43. if(w > high || v <= mid && lessThanOrEqual(src[v],src[w])) {

  44. dest[i] = src[v++];

  45. }else {

  46. dest[i] = src[w++];

  47. }

  48. }

  49. }

 

数据太多,递归太深 ->栈溢出?加大Xss?数据太多,数组太长 -> OOM?加大Xmx?

 

耐心不足,没跑出来.而且要将这么大的文件读入内存,在堆中维护这么大个数据量,还有内排中不断的拷贝,对栈和堆都是很大的压力,不具备通用性。

sort命令来跑

跑了多久呢?24分钟.

为什么这么慢?

粗略的看下我们的资源:

内存 jvm-heap/stack,native-heap/stack,page-cache,block-buffer 外存 swap + 磁盘 数据量很大,函数调用很多,系统调用很多,内核/用户缓冲区拷贝很多,脏页回写很多,io-wait很高,io很繁忙,堆栈数据不断交换至swap,线程切换很多,每个环节的锁也很多.

总之,内存吃紧,问磁盘要空间,脏数据持久化过多导致cache频繁失效,引发大量回写,回写线程高,导致cpu大量时间用于上下文切换,一切,都很糟糕,所以24分钟不细看了,无法忍受.

位图法

 
  1. privateBitSet bits;

  2.  

  3. publicvoid perform(

  4. String largeFileName,

  5. int total,

  6. String destLargeFileName,

  7. Castor<Integer> castor,

  8. int readerBufferSize,

  9. int writerBufferSize,

  10. boolean asc) throwsIOException {

  11.  

  12. System.out.println("BitmapSort Started.");

  13. long start = System.currentTimeMillis();

  14. bits = newBitSet(total);

  15. InputPart<Integer> largeIn = PartFactory.createCharBufferedInputPart(largeFileName, readerBufferSize);

  16. OutputPart<Integer> largeOut = PartFactory.createCharBufferedOutputPart(destLargeFileName, writerBufferSize);

  17. largeOut.delete();

  18.  

  19. Integer data;

  20. int off = 0;

  21. try {

  22. while (true) {

  23. data = largeIn.read();

  24. if (data == null)

  25. break;

  26. int v = data;

  27. set(v);

  28. off++;

  29. }

  30. largeIn.close();

  31. int size = bits.size();

  32. System.out.println(String.format("lines : %d ,bits : %d", off, size));

  33.  

  34. if(asc) {

  35. for (int i = 0; i < size; i++) {

  36. if (get(i)) {

  37. largeOut.write(i);

  38. }

  39. }

  40. }else {

  41. for (int i = size - 1; i >= 0; i--) {

  42. if (get(i)) {

  43. largeOut.write(i);

  44. }

  45. }

  46. }

  47.  

  48. largeOut.close();

  49. long stop = System.currentTimeMillis();

  50. long elapsed = stop - start;

  51. System.out.println(String.format("BitmapSort Completed.elapsed : %dms",elapsed));

  52. }finally {

  53. largeIn.close();

  54. largeOut.close();

  55. }

  56. }

  57.  

  58. privatevoid set(int i) {

  59. bits.set(i);

  60. }

  61.  

  62. privateboolean get(int v) {

  63. return bits.get(v);

  64. }

nice!跑了190秒,3分来钟. 以核心内存4663M/32大小的空间跑出这么个结果,而且大量时间在用于I/O,不错.

问题是,如果这个时候突然内存条坏了1、2根,或者只有极少的内存空间怎么搞?

外部排序

该外部排序上场了. 外部排序干嘛的?

内存极少的情况下,利用分治策略,利用外存保存中间结果,再用多路归并来排序;

map-reduce的嫡系.

1.分

内存中维护一个极小的核心缓冲区memBuffer,将大文件bigdata按行读入,搜集到memBuffer满或者大文件读完时,对memBuffer中的数据调用内排进行排序,排序后将有序结果写入磁盘文件bigdata.xxx.part.sorted. 循环利用memBuffer直到大文件处理完毕,得到n个有序的磁盘文件:

2.合

现在有了n个有序的小文件,怎么合并成1个有序的大文件?把所有小文件读入内存,然后内排?(⊙o⊙)… no!

利用如下原理进行归并排序:

我们举个简单的例子:

文件1:3,6,9 文件2:2,4,8 文件3:1,5,7

第一回合:文件1的最小值:3 , 排在文件1的第1行 文件2的最小值:2,排在文件2的第1行 文件3的最小值:1,排在文件3的第1行 那么,这3个文件中的最小值是:min(1,2,3) = 1 也就是说,最终大文件的当前最小值,是文件1、2、3的当前最小值的最小值,绕么?上面拿出了最小值1,写入大文件.

第二回合:文件1的最小值:3 , 排在文件1的第1行 文件2的最小值:2,排在文件2的第1行 文件3的最小值:5,排在文件3的第2行 那么,这3个文件中的最小值是:min(5,2,3) = 2 将2写入大文件.

也就是说,最小值属于哪个文件,那么就从哪个文件当中取下一行数据.(因为小文件内部有序,下一行数据代表了它当前的最小值)

最终的时间,跑了771秒,13分钟左右.

 

总结:就是分治的思想,每次从大文件中取出固定的N个数,然后排序输出到一个小文件中,依次类推直到所有的数据都写入到文件中。然后多个小的文件进行多路归并merge,将多个小文件合并为一个大的有序的文件。多次合并之后数据就有序了。

下面给出一个leetcode上的两路归并的题:

将两个有序的数组合并到一个大的数组nums1中,前提是num1中的元素个数m很大,可以放下nums1+nums2的所有数据

void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {int index = m + n -1;while (n != 0) {if (m != 0 && nums1[m-1] > nums2[n-1]) {nums1[index--] = nums1[m-1];m--;} else if (0 == m || nums1[m-1] <= nums2[n-1]) {nums1[index--] = nums2[n-1];n--;}}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463049.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jmeter连数据库

前提&#xff1a;jmeter不能直接连数据库&#xff0c;需要导入一个jar包 步骤&#xff1a; 1、右键线程组--添加--配置元件--JDBC Connection Configuration 2、jdbc的基本配置&#xff1a;可以修改jdbc配置的名称、随便填写变量名Variable Name&#xff0c;再填写最下面的数据…

设置窗口的光标,设置ToolBar,设置状态栏

代码 //ex_10Dlg.cpp : implementation file//#include "stdafx.h"#include "ex_10.h"#include "ex_10Dlg.h"#ifdef _DEBUG#definenew DEBUG_NEW#undefTHIS_FILEstaticcharTHIS_FILE[] __FILE__;#endif///////CAboutDlg dialog used for App Abo…

java 实例变量和类变量

当JAVA程序执行时&#xff0c;类的字节码文件就被加载到内存&#xff0c;如果该类没有创建对象&#xff0c;类的实例成员变量不会被分配内存&#xff0c;但是类中的类变量在类被加载到内存的时候&#xff0c;就分配了相应的内存空间。如果该类创建对象&#xff0c;那么不同的实…

MySQL主从复制作用和原理

该文章是转载的&#xff0c;但是原文中有些描述的不准确&#xff0c;进行了修改。 一、什么是主从复制? 主从复制&#xff0c;是用来建立一个和主数据库完全一样的数据库环境&#xff0c;称为从数据库&#xff1b;主数据库一般是准实时的业务数据库。 二、主从复制的作用 1、…

排列

例题 洛谷P1088 在c的函数库algorithm里有几个关于排列的函数 下一个全排列next_permutation next_permutation(num,numn)函数是求数组num中的前n个元素的下一个全排列&#xff0c;同时并改变num数组的值。 上一个全排列prev_permutation&#xff08;同理如上&#xff09; ac代…

windows mobile开发循序渐进(1)关于平台和工具

最近要进行windows mobile开发&#xff0c;很兴奋&#xff0c;因为之前对移动开发很感兴趣&#xff0c;并且做了一些B/S模式的开发&#xff0c;也做了一些包括WML和WEB移动开发的学习和开发&#xff0c;这次需要系统的整理一下windows mobile开发过程了&#xff0c;希望园子里的…

LC-BLSTM结构快速解读

参考文献如下&#xff1a; (1) A Context-Sensitive-Chunk BPTT Approach to Training Deep LSTM/BLSTM Recurrent Neural Networks for Offline Handwriting Recognition (2) Training Deep Bidirectional LSTM Acoustic Model for LVCSR by a Context-Sensitive-Chunk BPTT A…

mysql innodb缓存策略之Buffer Pool

The InnoDB Buffer Pool Innodb 持有一个存储区域叫做buffer pool是为了在内存中缓存数据和索引&#xff0c;知道innodb bufferpool怎么工作&#xff0c;和利用它读取频繁访问的数据&#xff0c;是mysql优化重要的方面。 理想状况下&#xff0c;把bufferpool的大小调整到足够大…

动态规划---背包问题分析

0/1背包 问题描述 有N件物品和一个容量为V的背包&#xff0c;第i件物品的体积为c[i]&#xff0c;价值为w[i]。求将哪些物品放进背包可以使物品价值总和最大&#xff08;有两种情况&#xff1a;不要求填满背包和填满背包&#xff09;。 每件商品只有一件&#xff0c;且只能选择放…

乱想想关于捕获异常后继续执行的实现。

好像是在csdn上看见的一个问题。 如下 现有A() B() C() 。。。。等很多个方法 但是他担心程序按 try { A(); B();//如这里抛异常 C()将不执行。 C(); //..... } catch (Exception e) …

spring boot 实战

https://windmt.com/2018/04/26/spring-cloud-full-reactive-microservices/ 这篇文章一级棒&#xff0c;实际操作一番&#xff0c;感觉有点理解微服务以及服务发现&#xff0c;服务间调用这几个概念。 但是对于响应式编程以及对于异步非阻塞场景等还是比较头大。 在实际操作中…

MySQL InnoDB的缓冲池之预读失效和缓存池污染

InnoDB的缓存池作用: 缓存表数据和索引数据,把磁盘上的数据加载到缓冲池中,避免每次都进行磁盘IO,起到加速访问的效果. LRU算法(Least recently used): 把入缓存池的页放在LRU的头部,作为最近访问的元素 页在缓冲池中的数据,把它放在队列的前面(情景一)页不在缓冲池中的数据,…

【database】database domain knowledge

Database Principles Database Systems Design Advanced Database Technology 一、Database principles Relational data model – relational algebra SQL – DDL, DML, DCL, ODBC(JDBC) Database design — Entity-Relationship model Relation normal forms XML – DTD, XM…

国内交流电频率和电压的历史渊源

国内交流电频率和电压的历史渊源 发布时间&#xff1a;2009-6-25 14:28 发布者&#xff1a;xiaochunyang 阅读次数&#xff1a;546商用交流电最早的频率是60Hz&#xff0c;电压是110V&#xff0c;其发明者Nikola Tesla是美国人&#xff08;移民&#xff09;并且是受…

Hbase Rowkey设计原则

Hbase是三维有序存储的&#xff0c;通过rowkey&#xff08;行键&#xff09;,column key(column family和qualifier)和TimeStamp(时间戳)这三个维度可以对HBase中的数据进行快速定位。 Hbase中Rowkey可以唯一标识一行记录&#xff0c;在Hbase查询的时候&#xff0c;有以下几种方…

explicit关键字详解

C explicit关键字详解 首先, C中的explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的, 跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式). 那么显示声明的构造函数和隐式声明…

Palm应用开发之四Palm 应用模型

本系列目录 Palm Web OS 简介 Palm 应用开发之一开发环境搭建 Palm 应用开发之二从Helloworld开始学习Palm开发Palm应用开发之三appinfo.json文件详解开发语言 应用使用的技术和Ajax使用的技术完全相似&#xff0c;palm webos 系统上建立应用没有专门的语言为其服务&#xff0c…

T-SQL 中ON和WHERE的区别

SQL中ON和WHERE的区别 数据库在通过连接两张或多张表来返回记录时&#xff0c;都会生成一张中间的临时表&#xff0c;然后再将这张临时表返回给用户。在使用left jion时&#xff0c;on和where条件的区别如下&#xff1a;1、 on条件是在生成临时表时使用的条件&#xff0c;它不管…

luogu3830 [SHOI2012]随机树

传送门&#xff1a;洛谷 题目大意&#xff1a;对于一个只有一个节点的二叉树&#xff0c;一次操作随机将这棵树的叶节点的下方增加两个节点。$n-1$次操作后变为$n$个叶节点的二叉树。求&#xff1a;&#xff08;1&#xff09;叶节点平均深度的期望值&#xff08;2&#xff09;树…

Mysql binlog应用场景与原理深度剖析

本文深入介绍Mysql Binlog的应用场景&#xff0c;以及如何与MQ、elasticsearch、redis等组件的保持数据最终一致。最后通过案例深入分析binlog中几乎所有event是如何产生的&#xff0c;作用是什么。 1 基于binlog的主从复制 Mysql 5.0以后&#xff0c;支持通过binary log(二进…