我正在尝试执行一个perl脚本来处理一个小的12 x 2文本文件(大约260个字节)和一个大的.bedgraph文件(大小至少为1.3 MB)。从这两个文件中,脚本输出一个新的床图文件。
我已经在其他3个.bedgraph文件上运行了这个脚本,但是我尝试在剩下的文件上运行它,这个过程不断获得Killed
。
perl脚本平均需要大约20分钟才能在每个.bedgraph
文件上运行。
我在本地计算机上运行perl脚本(而不是从服务器运行)。我正在使用Linux OS Ubuntu 12.04系统64位4GB RAM。
为什么我的perl脚本执行会被杀死,我该如何解决?
这是脚本:
# input file handle
open(my $sizes_fh, '<', 'S_lycopersicum_chromosomes.size') or die $!;
# output file handles
open(my $output, '+>', 'tendaysafterbreaker_output.bedgraph') or die $!;
my @array;
while(<$sizes_fh>){
chomp;
my ($chrom1, $size) = split(/\t/, $_);
@array = (0) x $size;
open(my $bedgraph_fh, '<', 'Solanum_lycopersicum_tendaysafterbreaker.bedgraph') or die $!;
while(<$bedgraph_fh>){
chomp;
my ($chrom2, $start, $end, $FPKM) = split(/\t/, $_);
if ($chrom1 eq $chrom2){
for(my $i = $start; $i < $end; $i++){
$array[$i] += $FPKM;
}
}
}
close $bedgraph_fh or warn $!;
my ($last_start, $last_end) = 0;
my $last_value = $array[0];
for (my $i = 1; $i < $#array; $i++){
my $curr_val = $array[$i];
my $curr_pos = $i;
# if the current value is not equal to the last value
if ($curr_val != $last_value){
my $last_value = $curr_val;
print $output "$chrom1\t$last_start\t$last_end\t$last_value\n";
$last_start = $last_end = $curr_pos;
} else {
$last_end = $i;
}
}
}
close $sizes_fh or warn $!;
答案 0 :(得分:5)
您正在尝试分配一个包含90,000,000个元素的数组。 Perl由于其灵活的键入和其他高级变量功能,使用了比预期更多的内存。
在我的(Windows 7)计算机上,一个只分配这样一个数组并且什么都不做的程序会占用3.5 GB的RAM。
有多种方法可以避免这种巨大的内存使用量。这是一对夫妇:
The PDL
module用于科学数据处理,旨在有效地将大量数字数组存储在内存中。这将改变分配和使用数组的语法(并且它以各种其他方式使用Perl的语法)。
DBM::Deep
是一个在文件中分配数据库的模块 - 然后允许您通过普通数组或散列访问该数据库:
use DBM::Deep;
my @array;
my $db = tie @array, "DBM::Deep", "array.db";
#Now you can use @array like a normal array, but it will be stored in a database.
答案 1 :(得分:2)
如果你知道一点C,那么将数组操作卸载到低级代码中非常简单。使用C数组占用的空间更少,速度更快。但是,你松散了像边界检查这样的好东西。这是一个使用Inline :: C的实现:
use Inline 'C';
...;
__END__
__C__
// note: I don't know if your data contains only ints or doubles. Adjust types as needed
int array_len = -1; // last index
int *array = NULL;
void make_array(int size) {
free(array);
// if this fails, start checking return value of malloc for != NULL
array = (int*) malloc(sizeof(int) * size);
array_len = size - 1;
}
// returns false on bounds error
int array_increment(int start, int end, int fpkm) {
if ((end - 1) > array_len) return 0;
int i;
for (i = start; i < end; i++) {
array[i] += fpkm;
}
return 1;
}
// please check if this is actually equivalent to your code.
// I removed some unneccessary-looking variables.
void loop_over_array(char* chrom1) {
int
i,
last_start = 0,
last_end = 0,
last_value = array[0];
for(i = 1; i < array_len; i++) { // are you sure not `i <= array_len`?
if (array[i] != last_value) {
last_value = array[i];
// I don't know how to use Perl filehandles from C,
// so just redirect the output on the command line
printf("%s\t%d\t%d\t%d\n", chrom1, last_start, last_end, last_value);
last_start = i;
}
last_end = i;
}
}
void free_array {
free(array);
}
最小测试代码:
use Test::More;
make_array(15);
ok !array_increment(0, 16, 2);
make_array(95_000_000);
ok array_increment(0, 3, 1);
ok array_increment(2, 95_000_000, 1);
loop_over_array("chrom");
free_array();
done_testing;
此测试用例的输出是
chrom 0 1 2
chrom 2 2 1
(删除了测试输出)。编译可能需要一秒钟,但之后它应该非常快。
答案 2 :(得分:0)
在从$bedgraph_fh
读取的记录中,$start
的典型值是多少?虽然散列每个条目的开销比数组多,但如果@array
以大量未使用的条目开头,则可以节省一些内存。例如,如果你有一个@array
的9千万个元素,但是前8000万个元素从未使用过,那么你很可能会更好地使用哈希值。
除此之外,我没有看到任何明显的例子,这些代码保留了它实现的算法不需要的数据,尽管,根据你的实际目标,可能有一个替代算法,不需要在内存中保存那么多数据。
如果你确实需要处理一组9千万个活跃数据元素,那么你的主要选择是购买大量内存或使用某种形式的数据库。在后一种情况下,我选择SQLite(通过DBD::SQLite)以简化和轻量级,但是YMMV。