Shell 按行上的字数对海量文件的行进行排序(理想情况下是并行排列)

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

我正在研究一种用于分析Facebook社交网络数据的社区检测算法。第一个任务,检测图中的所有派系,可以高效地并行完成,并给我留下如下输出:

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数相同的线条——可以任意排序。)

排序这些行最有效的方法是什么

请记住以下几点:

  • 我要排序的文件可能比机器的物理内存大
  • 我运行此功能的大多数机器都有多个处理器,因此理想的并行解决方案是
  • 理想的解决方案应该是一个shell脚本(可能使用排序),但我对python或perl(或任何语言)的简单解决方案持开放态度,只要它使任务变得简单即可
  • 从某种意义上说,这项任务非常简单——我不只是寻找任何旧的解决方案,而是寻找一个简单且最重要的是高效的解决方案
  • 更新2:最佳解决方案

    基于对建议的解决方案进行基准测试(见下文),以下是最佳解决方案(取自Vlad,Vlad反过来将其与此处建议的其他解决方案进行了改编)。它相当聪明,甚至不使用排序

    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