Shell 按行上的字数对海量文件的行进行排序(理想情况下是并行排列)
我正在研究一种用于分析Facebook社交网络数据的社区检测算法。第一个任务,检测图中的所有派系,可以高效地并行完成,并给我留下如下输出:Shell 按行上的字数对海量文件的行进行排序(理想情况下是并行排列),shell,unix,sorting,Shell,Unix,Sorting,我正在研究一种用于分析Facebook社交网络数据的社区检测算法。第一个任务,检测图中的所有派系,可以高效地并行完成,并给我留下如下输出: 17118 17136 17392 17064 17093 17376 17118 17136 17356 17318 12345 17118 17136 17356 17283 17007 17059 17116 这些行中的每一行都代表一个独特的组(节点ID的集合),我想按每行ID的数量降序排列这些行。在上面的例子中,输出应该是这样的: 17118 17
17118 17136 17392
17064 17093 17376
17118 17136 17356 17318 12345
17118 17136 17356 17283
17007 17059 17116
这些行中的每一行都代表一个独特的组(节点ID的集合),我想按每行ID的数量降序排列这些行。在上面的例子中,输出应该是这样的:
17118 17136 17356 17318 12345
17118 17136 17356 17283
17118 17136 17392
17064 17093 17376
17007 17059 17116
(领带——即ID数相同的线条——可以任意排序。)
排序这些行最有效的方法是什么
请记住以下几点:
for FILE in infile.* ; do
awk '{ print >sprintf("tmpfile.%05d.%s", NF, FILE) }' \
FILE=`basename $FILE` $FILE&
done
wait
ls -1r tmpfile.* | xargs cat >outfile
rm -f tmpfile.*
更新1:拟议解决方案的基准测试结果
为了进行基准测试,我选择了俄克拉荷马州Facebook网络中的小圈子。包含这些派系的未排序文件看起来就像我上面展示的第一个示例,包含46362546行,这使文件大小达到6.4GB。这些集团几乎平均分布在8个档案中。我正在测试的系统包含4个物理处理器,每个处理器有6个内核和一个12MB二级缓存,总共有24个内核。它还包含128 GB的物理内存。因为要排序的行被拆分为8个文件,所以大多数解决方案使用8个(或16个)并发进程
忽略第一个简单的方法,我对Vlad Romascanu(我选择的解决方案)的最后5个建议进行了基准测试
第一种解决方案效率不太高:
real 6m35.973s
user 26m49.810s
sys 2m14.080s
for FILE in infile.* ; do
awk '{ print >sprintf("tmpfile.%05d.%s", NF, FILE) }' \
FILE=`basename $FILE` $FILE&
done
wait
ls -1r tmpfile.* | xargs cat >outfile
rm -f tmpfile.*
我尝试使用解决方案2、3和4,它们使用FIFO文件,但它们都只使用一个排序过程,因此需要很长时间(因此我在它们完成之前就杀死了它们)/
最后一个解决方案是最快的:
请注意,此解决方案的用户时间为1m21s,比第一个解决方案的26分钟要好得多
awk '{print length,$0}' test.txt | sort -nr | cut -d" " -f2-
虽然排序可以绕过内存限制,但不确定它的性能如何。A简单的方法可能是:
awk '{ print NF " " $0 }' infile| sort -k1,1nr |
awk '{ $1=""; print $0 }' >outfile
这将使多达3个CPU保持忙碌状态<代码>排序不受可用物理内存量的限制,使用-S
和-T
开关配置要使用的内存量(-S
),然后在足够大(理想情况下是快速)的分区上使用临时目录(-T
)中的临时文件
如果可以通过细分排序阶段之前的工作来生成多个输入文件,则可以执行以下操作:
for FILE in infile.* ; do
awk '{ print NF " " $0 }' $FILE | sort -k1,1nr >$FILE.tmp&
done
wait
sort -k1,1nr -m infile.*.tmp | awk '{ $1=""; print $0 }' >outfile
rm -f infile.*.tmp
这将使用多达N*2个CPU;此外,最后一种排序(合并排序)效率很高
通过使用FIFO而不是中间文件,进一步细化以提高到N*2+1
的并行性,再次假设可以使用多个输入文件:
for FILE in infile.* ; do
mkfifo $FILE.fifo
awk '{ print NF " " $0 }' $FILE | sort -k1,1nr >$FILE.fifo&
done
sort -k1,1nr -m infile.*.fifo | awk '{ $1=""; print $0 }' >outfile
rm -f infile.*.fifo
如果不可能有多个输入文件,您可以模拟这些文件(增加I/O开销,该开销有望按可用进程数摊销):
因为我们使用模行号,所以我们有很好的局部性,文件系统缓存在理想情况下应该使在$PARALLELISM
进程中反复读取输入文件的成本接近于零
更好,只读取一次输入文件,并将输入行循环到多个排序管道中:
PARALLELISM=5 # I want 5 parallel instances
for N in `seq $PARALLELISM` ; do
mkfifo infile.$N.fifo1
mkfifo infile.$N.fifo2
sort -k1,1nr infile.$N.fifo1 >infile.$N.fifo2&
done
awk '{ print NF " " $0 >("infile." NR % '$PARALLELISM' ".fifo1") }' infile&
sort -k1,1nr -m infile.*.fifo2 | awk '{ $1=""; print $0 }' >outfile
rm -f infile.$N.fifo[12]
您应该测量$PARALLELISM
的各种值的性能,然后选择最佳值
编辑
如其他帖子所示,您当然可以使用cut
,而不是最后的awk
(即去掉第一列),以提高效率
编辑2
更新了您提供的文件名约定的所有脚本,并修复了上一版本中的错误
另外,使用新的文件名约定,如果I/O不是瓶颈,那么对dave
/niry
的解决方案进行非常细微的改动可能会更有效:
real 6m35.973s
user 26m49.810s
sys 2m14.080s
for FILE in infile.* ; do
awk '{ print >sprintf("tmpfile.%05d.%s", NF, FILE) }' \
FILE=`basename $FILE` $FILE&
done
wait
ls -1r tmpfile.* | xargs cat >outfile
rm -f tmpfile.*
为了创建高效的文件,我将执行如下操作,对文件进行两次解析:
在第一遍中,逐行读取,记录三件事:行号、文件偏移量和字数。这可以并行化,没有太多困难(对于文件中以“随机”行开始的作业,只需在单词后面添加相应的开始编号)
现在,按每行的字数对三个已录制内容的列表进行排序。然后迭代列表,寻找相应的起始偏移量
从性能的角度来看,所有的搜索可能都很慢,但内存消耗应该相对较少,每行只需要3个整数。我想知道这会有多快:
#!/bin/sh
rm -rf /tmp/fb
mkdir /tmp/fb
cd /tmp/fb
awk '{ print $0 > NF }'
ls | sort -nr | xargs cat
但是,它没有利用大量的核心。由于您实际上不需要排序,只需复制到存储桶中,您可以按令牌数拆分成文件,这将是最快的:
perl -ne 'split/\s+/;$t=$#_+1;open $f[$t], sprintf(">%09d",$t) if $f[$t] eq "";$f=$f[$t];print $f $_;'
cat `ls -1r 0*`
顺便说一句,磁盘将是瓶颈,核心和使用的问题并不重要。我不确定我是否理解这个问题
10 split the file into N subfiles, one for each core/cpu
20 sort each partial file using the solutions suggested in some of the answers here
30 once every file is split and sorted, get the first line from each file and put it into a temporary file
40 get the second line from each file, put them in a second temporary file
50 repeat until you have number of temp files == number of cores
60 GOTO 20