免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 7498 | 回复: 15
打印 上一主题 下一主题

perl的内存占用,求教&探讨 [复制链接]

论坛徽章:
1
程序设计版块每日发帖之星
日期:2015-10-07 06:20:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-04-01 14:48 |只看该作者 |倒序浏览
今天有个生物信息的任务,完成了,但是很不爽的搞死了电脑两次。于是全程播报我的思路和步骤,来论坛求教了,求拍砖:

1. 数据描述

有基因组数据如下,大写的是正常序列,小写的是repeat区域:
  1. >chr1
  2. GAATTCCAAAGCCAAAGATTGCATCAGTTCTGCTGCTATTTCCTCCTATCATTCTTTCTG
  3. ATGTTGAAAATGATATTAAGCCTAGGATTCGTGAATGGGAGAAGGTATTTTTGTTCATGG
  4. TAGTCATTGGAACCTGCTAGATTGTACACTTGACAATAACATATATTAATATTAGTGACC
  5. CCATTTTTAAATTTCCTAGGCTGGCATTGAACAAGACTATGTTAGTAGGATGTTGTTGAA
  6. GTATCCATGGATTCTTTCAACGAGTGTGATAGAGAACTACAGTCAAATGCTGTTGTTTTT
  7. CAACCAAAAAAGGGTAAGTAAAAAAGAATACTTACTATGCTGTGCCTCAAGTTCATGTTA

  8. TATTCAAATGCCGCAGCTCTGAtaaccactcttttctggaccaataaatggctgcttggt
  9. tcctctataggagatgtgtcgctggctgttcttgctatggtacccgcaaaaataattaaa

  10. cgtcttcgaAGGTTTGCAGCTGAGTATGTGGAATGTTCTGCATGCTCTCGAAGATGAACA
  11. GCATCTCTGGTGTCTCGCCGGAGTGCAAGGACTGCAGAGTTTAGGCCTGGGCTCCCTGAG
  12. CGGTTAGGTGGCATTTCCTGGTCTGGTCTCACCATTTCTATTTGCTGTAAGAGTTTTGTT
  13. >chr2
  14. .............
  15. .............
复制代码
2. 任务要求
按照小写的序列区域,将一个长的染色体分割成较短的片段,小写区域丢弃,大写片段短于500字符就丢弃。

3. 分析每一行的可能格式

1.
>chr1

2.
大写片段-----小写片段-----大写片段
小写----大写---小写(由于每行60字符,这里的大写片段显然不够500字符)
小写------大写
大写------小写

3.
一直小写
一直大写

4. 写代码如下:
  1. #!/usr/bin/perl -w
  2. #
  3. #Author **
  4. #This script is uesd for matching the repeat masked region, and then split
  5. #by them.

  6. use POSIX;

  7. my $repeat=$ARGV[0];#file repeat.fa
  8. my $out=$ARGV[1];#splited file
  9. my $min_seq_length=500;
  10. my $out_line_length=50;

  11. #repeat file load
  12. open REPEAT,"<$repeat" or die "hi:$!";
  13. my %chrom;
  14. my $base;
  15. while(<REPEAT>){
  16.         chomp;
  17.         if($_ =~ /^\>(.*?)$/){
  18.                 $base=$1;
  19.                 $chrom{$base}="";
  20.         }
  21.         else{
  22.                 $chrom{$base}.=$_;
  23.         }
  24. }
  25. close REPEAT;

  26. #print file splited by repeats
  27. open OUT,">$out" or die"Oh:$!";
  28. foreach $base (sort keys %chrom){

  29.         my @seq=split(//,$chrom{$base});
  30.         my $count=0;
  31.         my $start;
  32.         my $stop;
  33.         my $seq="";
  34.         my $switch=0;#1 == ATCG, 0 == N

  35.         foreach $a(@seq){
  36.                 if($a !~ /[atcg]/ && $switch == 0){  #new seq after repeat
  37.                         $switch=1;
  38.                         $start=$count+1;
  39.                         $seq.=$a;
  40.                 }
  41.                 elsif($a !~/[atcg]/ && $switch == 1){
  42.                         $seq.=$a;
  43.                 }
  44.                 elsif($a =~ /[atcg]/ && $switch == 1){
  45.                         $switch=0;
  46.                         $stop=$count;

  47.                         if(  ($stop - $start)   >=    $min_seq_length){
  48.                                 print OUT "\>$base\_$start\_$stop\n";
  49.                                 seq_print($seq,$out_line_length);
  50.                         }

  51.                         $seq="";
  52.                 }

  53.                 $count++;
  54.         }
  55.         #print the last seq
  56.         if($switch == 1){$stop=$count-1;}
  57.         if(     ($stop - $start)   >=    $min_seq_length){
  58.                 print OUT "\>$base\_$start\_$stop\n";
  59.                 seq_print($seq,$out_line_length);
  60.         }

  61. print "work on $base\n";
  62. }
  63. close OUT;



  64. sub seq_print{
  65.         my ($seq,$length)=@_;
  66.         my $step=floor(length($seq)/$length);
  67.         for(my $i=1;$i<=$step;$i++){
  68.                 my $out=substr($seq,($i-1)*$length,$length);
  69.                 print OUT "$out\n";
  70.         }
  71.         my $out=substr($seq,$step*$length);
  72.         print OUT "$out\n";
  73. }
复制代码
5. 测试小量数据通过
......


6. 大数据量,死机
于是在应用在了基因组上,数据量2G多

瞬间占用40G以上内存,服务器死机

7. 修改代码
想了想,我的@seq可能太过于恐怖了,于是修改代码如下:
  1. #!/usr/bin/perl -w
  2. #
  3. #Author **
  4. #This script is uesd for matching the repeat masked region NNNNN, and then split
  5. #by them.

  6. use POSIX;

  7. my $repeat=$ARGV[0];#file repeat.fa
  8. my $out=$ARGV[1];#splited file
  9. my $min_seq_length=500;
  10. my $out_line_length=50;

  11. #repeat file load
  12. open REPEAT,"<$repeat" or die "hi:$!";
  13. my %chrom;
  14. my $base;
  15. my $count=0;
  16. while(<REPEAT>){
  17.         chomp;
  18.         if($_ =~ /^\>(.*?)$/){
  19.                 $base=$1;
  20.                 $count=0;
  21.         }
  22.         else{
  23.                 $chrom{$base}[$count]=$_;
  24.                 $count++;
  25.         }
  26. }
  27. close REPEAT;


  28. open OUT,">$out" or die"Oh:$!";
  29. foreach $base (sort keys %chrom){

  30.         my $array_hash=$chrom{$base};
  31.         my $count=0;
  32.         my $start;
  33.         my $stop;
  34.         my $seq="";
  35.         my $switch=0;#1 == ATCG, 0 == N

  36.         foreach $name(@$array_hash){
  37.           my @line=split(//,$name);
  38.           foreach $a(@line){
  39.                 if($a !~ /[atcg]/ && $switch == 0){  # first base of seq after repeat
  40.                         $switch=1;
  41.                         $start=$count+1;
  42.                         $seq.=$a;
  43.                 }
  44.                 elsif($a !~ /[atcg]/ && $switch == 1){ #continue seq
  45.                         $seq.=$a;
  46.                 }
  47.                 elsif($a =~ /[atcg]/ && $switch == 1){ #come across repeat
  48.                         $switch=0;
  49.                         $stop=$count;

  50.                         if(  ($stop - $start)   >=    $min_seq_length){
  51.                                 print OUT "\>$base\_$start\_$stop\n";
  52.                                 seq_print($seq,$out_line_length);
  53.                         }

  54.                         $seq="";
  55.                 }

  56.                 $count++;
  57.           }
  58.         undef(@line);

  59.         }
  60.         #print the last seq
  61.         if($switch == 1){$stop=$count-1;}
  62.         if(     ($stop - $start)   >=    $min_seq_length){
  63.                 print OUT "\>$base\_$start\_$stop\n";
  64.                 seq_print($seq,$out_line_length);
  65.         }

  66. print "work on $base\n";
  67. }
  68. close OUT;



  69. sub seq_print{
  70.         my ($seq,$length)=@_;
  71.         my $step=floor(length($seq)/$length);
  72.         for(my $i=1;$i<=$step;$i++){
  73.                 my $out=substr($seq,($i-1)*$length,$length);
  74.                 print OUT "$out\n";
  75.         }
  76.         my $out=substr($seq,$step*$length);
  77.         print OUT "$out\n";
  78. }
复制代码
8. 求教
这次占用了大概4.3G的内存,程序顺利跑完,但是我依然不明白哪里需要4G内存了。
在我看来,这两行:
$seq="";
undef(@line);
已经释放了内存,应该只是%chrom存储的2G序列而已了。

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
2 [报告]
发表于 2011-04-01 14:55 |只看该作者
2G 的原始数据才占 4G 的内存,已经很省了啊?

论坛徽章:
1
程序设计版块每日发帖之星
日期:2015-10-07 06:20:00
3 [报告]
发表于 2011-04-01 15:01 |只看该作者
回复 2# zhlong8


    这个....这样啊....
    那另外2G内存被什么吃掉了?

    或者说,
    为什么4G就很省了?

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
4 [报告]
发表于 2011-04-01 15:06 |只看该作者
本帖最后由 zhlong8 于 2011-04-01 15:07 编辑

将一个长的染色体分割成较短的片段,小写区域丢弃,大写片段短于500字符就丢弃。

这个用 RE 就能做了啊?你为什么一个一个字符处理?

@seq 那里 split 成单个字符每1K个占用内存是 48K,如果你一段基因超长的话……

Perl 的字符串可变,所以可能分配的内存比需要的多,而且是以2^n 的速度扩大的,所以平均长度越长浪费的也就越多

数据结构不也要占用内存,建一个空空的数组或 hash 难道就不需要空间了?

论坛徽章:
1
程序设计版块每日发帖之星
日期:2015-10-07 06:20:00
5 [报告]
发表于 2011-04-01 15:24 |只看该作者
回复 4# zhlong8


谢谢说明!


将一个长的染色体分割成较短的片段,小写区域丢弃,大写片段短于500字符就丢弃。
这个用 RE 就能做了啊?你为什么一个一个字符处理?

我只能说是,想到什么就写什么了,另,RE是什么?


@seq 那里 split 成单个字符每1K个占用内存是 48K,如果你一段基因超长的话……

是的,恐怖的长,2G只有10个基因组,平均一个200MB. 看来这就是我第一次程序挂掉的原因


Perl 的字符串可变,所以可能分配的内存比需要的多,而且是以2^n 的速度扩大的,所以平均长度越长浪费的也就越多
数据结构不也要占用内存,建一个空空的数组或 hash 难道就不需要空间了?

$seq的长度经常会有几千上万,一直在变,那这就是原因了?
我是不是可以理解为:

$a="A";
$b="B";
$c="CCCCC";


$a="AA";
$b="BB";
$c="CC";

前者比后者 消耗的内存多?

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
6 [报告]
发表于 2011-04-01 15:38 |只看该作者
对于很长的数据按行存储和处理比较高效。

说用 RE 指的是对于每一行的数据可以用正则表达式来判断分割。

对于比较短的字符串不需要考虑这么多,一个变量保存字符串的长度是曾经最长的长度,即使赋值了个比较短的字符串,相当于剩余的空间就是缓冲区了。用 undef 可以释放

论坛徽章:
1
程序设计版块每日发帖之星
日期:2015-10-07 06:20:00
7 [报告]
发表于 2011-04-01 15:46 |只看该作者
回复 6# zhlong8


谢谢,变量占用内存大概明白了...


    RE指,类似这样的处理么?
     if (   $line=~/([ATCG]*)([atcg]*)([ATCG]*)/ ) {
                $1;
                $2;
                $3;
   }

其实一开始我是有想这么写的,但是还需要计算大写片段在染色体上的位置, 每一列的可能又多,写到一半就懒了...

论坛徽章:
0
8 [报告]
发表于 2011-04-02 11:43 |只看该作者
本帖最后由 longbow0 于 2011-04-02 14:07 编辑

可能会占用巨大内存的方法。具体消耗的内存与每条序列长度相关。

  1. #!/usr/bin/perl

  2. use strict;
  3. use warnings;

  4. use Bio::SeqIO;

  5. my $seqi = Bio::SeqIO->new(
  6.     -file => 'chr.fasta',
  7.     -format => 'fasta',
  8. );

  9. while ( my $seq = $seqi->next_seq ) {
  10.     my @sseqs = split /[a-z]+/, $seq->seq; # 提取大写序列
  11.    
  12.     # 或者更精确一点
  13.     # my @sseqs = split /[atcg]+/, $seq->seq;

  14.     for my $sseq ( @sseqs ) {
  15.         next if length( $sseq ) < 500;

  16.         print $sseq, "\n"; # 直接打印
  17.     }
  18. }

复制代码

论坛徽章:
0
9 [报告]
发表于 2011-04-02 16:44 |只看该作者
不耗内存版,行处理:

  1. use strict;
  2. use warnings;

  3. my $repeat=$ARGV[0];#file repeat.fa
  4. my $out=$ARGV[1];#splited file
  5. my $min_seq_length=500;
  6. my $out_line_length=50;

  7. #repeat file load
  8. open REPEAT,"<$repeat" or die "hi:$!";
  9. open OUT,">$out" or die"Oh:$!";
  10. my $DNA="";
  11. my $ok_flag=1;
  12. my $txt;
  13. while($txt=<REPEAT>){
  14.   chomp $txt;
  15.   next unless($txt);
  16.   if($txt =~ /^\>/){  #new DNA, clean the old one
  17.     if($ok_flag) {
  18.             while(length($DNA)>$out_line_length) {
  19.                     print OUT substr($DNA,0,$out_line_length,""),"\n";
  20.             }
  21.             print OUT $DNA,"\n";
  22.             $ok_flag=0;
  23.     }
  24.     elsif(length($DNA)>=$min_seq_length) {
  25.             while(length($DNA)>$out_line_length) {
  26.                     print OUT substr($DNA,0,$out_line_length,""),"\n";
  27.             }
  28.             print OUT $DNA,"\n";
  29.     }
  30.     $DNA="";
  31.     print OUT "$txt\n";
  32.   }
  33.   elsif($txt=~/[atcg]/){
  34.          
  35.                 if($txt=~/^([ATCG]+)/) {
  36.                         $DNA .=$1;
  37.                 }
  38.                
  39.                 if($ok_flag) {
  40.             while(length($DNA)>$out_line_length) {
  41.                     print OUT substr($DNA,0,$out_line_length,""),"\n";
  42.             }
  43.             print OUT $DNA,"\n";       
  44.             $ok_flag = 0;
  45.     }
  46.     else {
  47.                         if(length($DNA)>=$min_seq_length) {
  48.                     while(length($DNA)>$out_line_length) {
  49.                             print OUT substr($DNA,0,$out_line_length,""),"\n";
  50.                     }
  51.                     print OUT $DNA,"\n";       
  52.             }          
  53.     }
  54.                 $DNA="";
  55.                 if($txt=~/([ATCG]+)$/) {
  56.                         $DNA = $1;
  57.           }         
  58.         }
  59.         else {
  60.                 $DNA .= $txt;
  61.                
  62.                 if($ok_flag) {
  63.             while(length($DNA)>$out_line_length) {
  64.                     print OUT substr($DNA,0,$out_line_length,""),"\n";
  65.             }
  66.     }
  67.     else {
  68.                         if(length($DNA)>=$min_seq_length) {
  69.                     while(length($DNA)>$out_line_length) {
  70.                             print OUT substr($DNA,0,$out_line_length,""),"\n";
  71.                     }
  72.                     $ok_flag = 1;
  73.             }          
  74.     }               
  75.         }
  76. }

  77. close REPEAT;
  78. close OUT;

复制代码

论坛徽章:
1
程序设计版块每日发帖之星
日期:2015-10-07 06:20:00
10 [报告]
发表于 2011-04-04 08:08 |只看该作者
回复 8# longbow0


    谢谢了, 跑了一遍,很快,1.6 G~2.4G内存之间,看来Seq:IO很强大啊..
    只是,我的scripts其实还计算了每一段大写片段的在基因组上的位置...用split ...不好记录吧:)
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP