【学习】大文件统计与排序(转载)

学习:大文件统计与排序

这篇主要记录一下学习陈硕同学的对下面这道题的算法思想与代码。

题目是这样的:

有10个文件,每个文件1G,每个文件的每行存放的都是用户的query(请自己随机产生),每个文件的query都可能重复。要求你按照query的频度排序。

(当然,这里的重点是大文件,所以10个1G的文件,或者1个10G的文件,原理都是一样的)

陈硕的代码在这里:

https://gist.github.com/4009225

这是一段非常漂亮的代码,解法与代码都非常值得一看。

【解法】

基本步骤就是不断读入文件,并做初步统计,到了某个内存的极限时写出文件,写的方式是按query的哈希值分配到10个不同的文件中,直到读完所有文件内容,然后把这10个文件中的query按count排序,并10路归并排序出最后结果。

shuffle

从命令行传入输入文件,逐行读入,并存放在一个hashmap中,边读边统计<query, count>,到map的size到达指定size时(10*1000*1000, 主要是考虑内存容量),把这个hashmap的内容先写出去,写到10个文件的第hash(query) % 10个中去,这保证了相同的query肯定在同一个文件中。 这样,直到把文件读完。所以如果输入文件总大小为10G的话,每个文件大小为 <1G (因为相同的query并合并了),可以进行单文件内存内处理。注意此时虽然相同的query在同一文件中,他们可能是分布在好几个地方的,如:

query1 10
query2 5
query3 3
query1 3
query4 3
query 2 7

reduce

把每个文件中相同query合并,并将query按count排序。

merge

10个有序的文件,通过归并排序得到最后的结果。归并的方式是通过一个10个元素的堆,相比于两两迭代归并排序,这大大减少了读文件的时间。

【运行】

该程序只在linux下运行,并需要boost,ubunut下,先安装boost:

apt-get install libboost-dev

然后编译,该程序用到了c++ 0x的feature,所以需要-std=c++0x:

g++ sort.cpp -o sort -std=c++0x

在运行前,需要准备输入数据,这里用lua随机产生:(https://gist.github.com/4045503

-- updated version, use a table thus no gc involved
local file = io.open("file.txt", "w")
local t = {}
for i = 1, 500000000 do
        local n = i % math.random(10000)
        local str = string.format("This is a number %d\n", n)
        table.insert(t, str)
        if i % 10000 == 0 then
                file:write(table.concat(t))                t = {}
        end
end

好,开始运行:

sort file.txt

结果如下:

$ time sort file.txt
processing file.txt
shuffling done
reading shard-00000-of-00010
writing count-00000-of-00010
reading shard-00001-of-00010
writing count-00001-of-00010
reading shard-00002-of-00010
writing count-00002-of-00010
reading shard-00003-of-00010
writing count-00003-of-00010
reading shard-00004-of-00010
writing count-00004-of-00010
reading shard-00005-of-00010
writing count-00005-of-00010
reading shard-00006-of-00010
writing count-00006-of-00010
reading shard-00007-of-00010
writing count-00007-of-00010
reading shard-00008-of-00010
writing count-00008-of-00010
reading shard-00009-of-00010
writing count-00009-of-00010
reducing done
merging done

real 19m18.805s
user 14m20.726s
sys 1m37.758s

在我的32位Ubuntu11.10虚拟机上, 分配了1G内存,1个2.5G的CPU core, 处理一个15G的文件,花了19m分钟。

【学习】

  • 把query按哈希值分配到不同的文件,保证想通过query在同一个文件中,漂亮
  • 10路归并排序,用一个最大(小)堆来做,减少了文件读写,漂亮
  • LocalSink, Shuffler, Source等很小的类来封装、解耦一些特别的的任务,结构十分漂亮
  • 一些我不熟悉的知识:
    • __gnu_cxx::__sso_string, gnu short string optimization, 这里有跟更多的说明
    • boost::function , boost::bind
    • 使用map的[] operator时,插入数据根据默认构造函数初始化,对于int默认则是为0
    • C++ 0x的for each:for (auto kv : queries)
    • boost::noncopyable:不能被copy的类从此继承
    • std::hash<string>(): 返回一个针对string的hash functor
    • boost::ptr_vector:boost针对每个container都一共了一个ptr的版本,这个比单纯的使用vector<shared_ptr<T>>要更高效
    • unlink: delete的一个文件
    • std::unordered_map<string, int64_t> queries(read_shard(i, nbuckets)):使用了move sematic,不然效率会很低
    • std::pair定义了 < operator,先比较第一个元素
时间: 2024-08-16 17:44:54

【学习】大文件统计与排序(转载)的相关文章

用java实现大文件分割、排序、合并

import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.Collections; import java.util.Iterator; import java

python学习——大文件分割与合并

在平常的生活中,我们会遇到下面这样的情况: 你下载了一个比较大型的游戏(假设有10G),现在想跟你的同学一起玩,你需要把这个游戏拷贝给他. 然后现在有一个问题是文件太大(我们不考虑你有移动硬盘什么的情况),假设现在只有一个2G或4G的优盘,该怎么办呢? 有很多方法,例如winrar压缩的时候分成很多小卷,这里不累述. 在学习python之后,我们自己就可以解决这个问题啦. 我们可以自己写一个脚本去分割合并文件,将文件分割成适合优盘大小的小文件,在拷贝,然后再合并. 下面是文件分割脚本: 1 im

PHP几个几十个G大文件数据统计并且排序处理

诸多大互联网公司的面试都会有这么个问题,有个4G的文件,如何用只有1G内存的机器去计算文件中出现次数最多的数字(假设1行是1个数组,例如QQ号 码).如果这个文件只有4B或者几十兆,那么最简单的办法就是直接读取这个文件后进行分析统计.但是这个是4G的文件,当然也可能是几十G甚至几百G的文 件,这就不是直接读取能解决了的. 同样对于如此大的文件,单纯用PHP做是肯定行不通的,我的思路是不管多大文件,首先要切割为多个应用可以承受的小文件,然后批量或者依次分析统计小文件后再把总的结果汇总后统计出符合要

第五章 shell学习之文件的排序、合并和分割

sort命令 sort [选项] [输入文件] 选项: -c 测试文件是否已经排序,如果未被排序则输出第一个未被排序的记录 -k 指定排序的域 -m 合并两个已排序的文件,合并的文件也已经排序,如sort -m a1 a2,a1的记录被有序的插入a2 -n 根据数字的大小进行排序,一般放在域号后,如-k3n -o 将输出重定向到指定文件 -r 将排序结果逆向显示 -t 改变域分割符,如-t: -u 去除结果中的重复行 sort和awk联合 例: [[email protected] tmp]#

统计大文件里单词

转载统计大文件里,频数最高的10个单词,(C# TPL DataFlow版) 最近公司搞了一个写程序的比赛,要求从2G的文件里统计出出现频率最高的10个单词. 最开始的想法是使用字典树,后来发现字典树更适合用在找前缀上,在查找没有hash表效率高. 之后使用Hash表+DataFlow完成了功能,2G的文件处理在20秒以内(其实我有信心优化到10秒以内,但是太折腾了). 这是我的设计图: 为什么要形成那么多结果?因为我不想写锁,写锁会降低很多效率,而且也失去了线程的意义,每个线程做自己的工作,

大文件/数据网络传输方法总结(转载)

网络编程中不免会遇到需要传输大数据.大文件的情况,而由于socket本身缓冲区的限制,大概一次只能发送4K左右的数据,所以在传输大数据时客户端就需要进行分包,在目的地重新组包.而实际上已有一些消息/通讯中间件对此进行了封装,提供了直接发送大数据/文件的接口:除此之外,利用共享目录,ftp,ssh等系统命令来实现大文件/数据也不失为一种好的方法. 1.基础的基于socket进行传输 基础的基于socket进行传输关键在于控制,需要自己行分包和组包. ////////////////////////

C# 学习黑马.Net视频教程,大文件拷贝

设计器代码: namespace 大文件拷贝 { partial class Form1 { /// <summary> /// 必需的设计器变量. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// 清理所有正在使用的资源. /// </summary> /// <param name="disposing&

使用shell统计字符串出现的次数,并从大到小进行排序显示

需求说明:对文档中的字符串进行统计,并进行排序后(从大到小),显示出前2条记录 构造的test.txt内容如下: a 12332 a 2323 a 234234 b 232 c 23432 c 34 统计并排序的方法: [[email protected] ~]$ awk '{a[$1]++} END {for(i in a){print i,a[i] | "sort -r -k 2"}}' test.txt > r.txt 执行结果: a 3 c 2 b 1 打印前两条记录的方

可以对大文件排序的排序算法

Z-Tree是一个可以对大数据排序的数据结构.Z-Tree排序的时间复杂度是O(n). Z-Tree可以取代Hash表实现关键字(Key)到值(Value)的映射. Z-Tree Demo展示了怎样用Z-Tree对若干GB的大文件排序. Z-Tree Demo同时展示了怎样用Z-Tree来实现大量关键字(Key)到值(Value)的映射并且根据关键字(Key)快速找到相应的值. Z-Tree Demo还展示了怎样用Z-Tree来从大量的字符串中查找最大匹配子字符串. 另外Z-Tree Demo包