2014-07-17 82 views
1

我正在運行一個perl腳本,該腳本崩潰時出現內存不足錯誤。 腳本在具有128 Gb內存的計算機上運行在Ubuntu Linux下。在提交時,大部分內存都可用,但腳本隨着其使用的內存超過略高於8Gb的值而死亡。機器(和操作系統)是64位的。Perl內存不足但存在大量可用內存

我一直在尋找perl內存分配的限制,但我發現的唯一限制是機器內存,在這種情況下,即使沒有考慮交換分區,也有很多。

這是我第二次遇到這個問題。我第一次使用不同的腳本,發生了同樣的事情。有沒有人有解釋?我看到的唯一可能是perl對內存分配有一些限制,但是我在網上搜索的所有結果似乎與這種可能性相矛盾。

在此先感謝

編輯1: 操作系統是Fedora Linux系統,而不是Ubuntu Linux操作系統。對不起,我感到困惑。

編輯2: 下面是導致錯誤的代碼部分:

open($psFullInput, "<", "fullPsIn.dat"); 
$counter = <$psFullInput>; # First element is counter of spectra 
while ($line = <$psFullInput>) { 
    @elems = split(" ",$line); 
    $xx = shift(@elems); 
    $yy = shift(@elems); 
    $freq = shift(@elems); 
    $psStored[$xx][$yy] = []; 
    push(@{$psStored[$xx][$yy]}, @elems); 
} 
close($psFullInput); 

前面的腳本在做類似的事情,不同的是數組元素不是從一個文件,但在那裏讀一些計算的結果。

EDIT 3:perl的-V的 結果:

Summary of my perl5 (revision 5 version 16 subversion 3) configuration: 

    Platform: 
    osname=linux, osvers=3.10.9-200.fc19.x86_64, archname=x86_64-linux-thread-multi 
    uname='linux buildvm-01.phx2.fedoraproject.org 3.10.9-200.fc19.x86_64 #1 smp wed aug 21 19:27:58 utc 2013 x86_64 x86_64 x86_64 gnulinux ' 
    config_args='-des -Doptimize=-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -Dccdlflags=-Wl,--enable-new-dtags -Dlddlflags=-shared -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -Wl,-z,relro -DDEBUGGING=-g -Dversion=5.16.3 -Dmyhostname=localhost [email protected] -Dcc=gcc -Dcf_by=Red Hat, Inc. -Dprefix=/usr -Dvendorprefix=/usr -Dsiteprefix=/usr/local -Dsitelib=/usr/local/share/perl5 -Dsitearch=/usr/local/lib64/perl5 -Dprivlib=/usr/share/perl5 -Dvendorlib=/usr/share/perl5/vendor_perl -Darchlib=/usr/lib64/perl5 -Dvendorarch=/usr/lib64/perl5/vendor_perl -Darchname=x86_64-linux-thread-multi -Dlibpth=/usr/local/lib64 /lib64 /usr/lib64 -Duseshrplib -Dusethreads -Duseithreads -Dusedtrace=/usr/bin/dtrace -Duselargefiles -Dd_semctl_semun -Di_db -Ui_ndbm -Di_gdbm -Di_shadow -Di_syslog -Dman3ext=3pm -Duseperlio -Dinstallusrbinperl=n -Ubincompat5005 -Uversiononly -Dpager=/usr/bin/less -isr -Dd_gethostent_r_proto -Ud_endhostent_r_proto -Ud_sethostent_r_proto -Ud_endprotoent_r_proto -Ud_setprotoent_r_proto -Ud_endservent_r_proto -Ud_setservent_r_proto -Dscriptdir=/usr/bin -Dusesitecustomize' 
    hint=recommended, useposix=true, d_sigaction=define 
    useithreads=define, usemultiplicity=define 
    useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef 
    use64bitint=define, use64bitall=define, uselongdouble=undef 
    usemymalloc=n, bincompat5005=undef 
    Compiler: 
    cc='gcc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64', 
    optimize='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic', 
    cppflags='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include' 
    ccversion='', gccversion='4.8.2 20131017 (Red Hat 4.8.2-1)', gccosandvers='' 
    intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678 
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16 
    ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8 
    alignbytes=8, prototype=define 
    Linker and Libraries: 
    ld='gcc', ldflags =' -fstack-protector' 
    libpth=/usr/local/lib64 /lib64 /usr/lib64 
    libs=-lresolv -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lpthread -lc -lgdbm_compat 
    perllibs=-lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread -lc 
    libc=, so=so, useshrplib=true, libperl=libperl.so 
    gnulibc_version='2.17' 
    Dynamic Linking: 
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,--enable-new-dtags -Wl,-rpath,/usr/lib64/perl5/CORE' 
    cccdlflags='-fPIC', lddlflags='-shared -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -Wl,-z,relro ' 


Characteristics of this binary (from libperl): 
    Compile-time options: HAS_TIMES MULTIPLICITY PERLIO_LAYERS 
         PERL_DONT_CREATE_GVSV PERL_IMPLICIT_CONTEXT 
         PERL_MALLOC_WRAP PERL_PRESERVE_IVUV USE_64_BIT_ALL 
         USE_64_BIT_INT USE_ITHREADS USE_LARGE_FILES 
         USE_LOCALE USE_LOCALE_COLLATE USE_LOCALE_CTYPE 
         USE_LOCALE_NUMERIC USE_PERLIO USE_PERL_ATOF 
         USE_REENTRANT_API USE_SITECUSTOMIZE 
    Built under linux 
    Compiled at Nov 11 2013 12:36:47 
    %ENV: 
    PERL5LIB="/home/parisia/lib/perl5/lib64/perl5" 
    @INC: 
    /home/parisia/lib/perl5/lib64/perl5 
    /usr/local/lib64/perl5 
    /usr/local/share/perl5 
    /usr/lib64/perl5/vendor_perl 
    /usr/share/perl5/vendor_perl 
    /usr/lib64/perl5 
    /usr/share/perl5 
. 

EDIT 4: 這裏是一個短碼再現的問題。正如你所看到的,我只是填滿了記憶。

use strict; 
use warnings; 

my ($xx,$yy); # Coordinate variables 
my (@elems); # Array of elements to be stored on each matrix position 

# Generate an array of 3000 floating point values. 
# The list will be added to each array element. In the true script 
# of course, each element has a list of completely different values. 
# Here I use the same list of values for simplicity. 
for ($xx = 1; $xx < 3000; $xx++) { 
    push(@elems, 1+$xx/10000); 
} 

# Fill in each matrix element with the generated array 
my @psStored; 
for ($xx = 0; $xx < 300; $xx++) { 
    print "Row [$xx]\n"; 
    for ($yy = 0; $yy < 300; $yy++) { 
    push(@{$psStored[$xx][$yy]}, @elems); 
    } 
} 

這就是「自由」在崩潰之前(也有在計算機上運行一些並行進程)結果:

[[email protected] ~]$ free 
      total  used  free  shared buffers  cached 
Mem:  132015788 46395504 85620284   0  217192 16979772 
-/+ buffers/cache: 29198540 102817248 
Swap: 268435452   0 268435452 

更新1

我進一步調查。建議我使用Devel :: Size並檢查$ psStored的大小。輸出的最後幾行(打印在$ YY每個週期結束時的大小)爲:

Row [226] 
8772773032 
Row [227] 
8811419600 
Row [228] 
Out of memory! 

當腳本給出錯誤的過程的大小是: VIRT:8943960,RES:8.406克

然而,我試圖下面的腳本,其分配一個給定的大小(千兆字節)的字符串的命令行上指定:

use strict; 
use warnings; 

my $size = $ARGV[0]; 
print "$size GB "; 
$size = int($size * 1000000000); 
print "($size bytes)...\n"; 
my $var = "x" x $size; 
print "Allocated\n"; 

此腳本具有大的分配沒有問題。例如,我可以請求30 GB,並且在腳本完成之前從「top」獲得以下輸出: VIRT:56.004g,RES:0。054t

因此,在處理數組時,有一些干擾分配,但我不明白髮生了什麼。我也嘗試過使用哈希,但我在8-9GB左右得到相同的限制。

+0

您的意思是GB(字節)而不是Gb(位),對不對?你能告訴我們這個腳本使用的大部分8 GB的分配是什麼嗎?是很多小塊還是一些大塊?有沒有想過在它耗盡的時候它要分配多少內存?你有沒有試過在調試器中運行? –

+2

嘗試編輯您的問題並粘貼在'perl -V'的輸出中 –

+0

@JohnZwinck 是千兆字節。我認爲它是小塊。我將編輯該問題以放置出現錯誤的代碼部分。 – Andrea

回答

1

我懷疑$xx$yy的值是相當大和稀疏(即它們之間有很大的差距)。這意味着Perl必須爲所有中間值創建數組元素,即使它們中沒有數據。

數據結構的設計主要取決於您在構建數據結構後如何使用它以及如何訪問它。最節省空間的方法是使用一個哈希,所以,如果你有

$xx = 1024 
$yy = 2048 

然後,而不是存儲在$ps_stored[1024][2048]頻率(這將創建通過$ps_stored[0]通過對$ps_stored[1023],並$ps_stored[1024][0]$ps_stored[1024][2047]和使他們空)的你可以將它存儲在散列$ps_stored{'1024,2048'},它根本沒有空間。

既然你沒有說出你將如何使用這些數據,我不知道它是否可以像這樣工作,但是這裏有一些代碼來替代你的構建哈希的方式。

use strict; 
use warnings; 
use autodie; 

my %ps_stored; 

open my $ps_full_input, '<', 'fullPsIn.dat'; 
my $counter = <$ps_full_input>; # First element is counter of spectra 

while (<$ps_full_input>) { 
    my @elems = split; 
    my ($xx, $yy, $freq) = @elems; 
    push @{ $ps_stored{"$xx,$yy"} }, \@elems; 
} 

close $ps_full_input; 

注意以下

  • 您必須總是檢查的open調用成功。忽略失敗的open會使你的程序無緣無故地產生廢話。你可以寫一個明確的

    open my $ps_full_input, '<', 'fullPsIn.dat' or die $!; 
    

    ,或者你可以use autodie在你的程序,這是有用的頂部,如果有超過兩的open通話更在你的代碼

  • 您必須總是use strictuse warnings位於每個Perl程序的頂部,並使用my聲明所有變量儘可能接近其第一個使用位置。因爲你的代碼是一個示例,目前尚不清楚是否有到位strictwarnings,但目前還沒有宣佈在所有這樣的東西是錯誤

  • 誰是用來使用Perl會感謝你只用較低的人本地標識符中的字母,數字和下劃線。大寫字母被保留用於封裝和模塊名稱,如Data::Dumper

  • 在您將push放在它上面之前,不需要預先設置標量值來引用空數組。一個數組將會是autovivified第一次使用push只要標量仍然是undef。例如

    my $aref; 
    push @{ $aref }, 1, 2, 3; 
    

    具有如

    my $aref; 
    $aref = []; 
    push @{ $aref }, 1, 2, 3; 
    
  • 我有推一個參考@elems陣列到ps_stored列表中的相同的效果。我不知道$xx$yy的特定值是否可以多次出現,但是如果他們這樣做,那麼您的方式將會將所有不同的@elems集合推送到單個列表中,這可能難以分裂成單個集合。如果你把一個參考,而不是那麼它們仍然是獨立的

我希望這有助於

+0

嗨,謝謝你的回答。我讀的是一個密集的矩陣:約三分之一的元素包含數據。存儲在每個矩陣元素中的數據是大約3000個浮點值的列表。對於存儲這種數據的最佳方式,我並不是專家,但我已經嘗試過使用哈希,它看起來好像在我的情況下沒有太大的區別。當然,我可能犯了錯誤。 – Andrea

+0

該代碼在開始時具有通常的「嚴格使用」和「使用警告」。事實上,我並沒有檢查正確的開放,但即使進行適當的檢查,我也達到了相同的內存限制。所有變量都在我顯示的代碼塊之前正確聲明。 – Andrea

+0

@Andrea:好吧,所以你浪費了大約2/3的數組元素,這相當於分配內存的25%。這似乎可以接受。我想知道'$ xx'和'$ yy'值的範圍。你還應該安裝['Devel :: Size'](https://metacpan.org/module/Devel::Size)並且寫入'print total_size($ psStored),「\ n」'作爲'而'循環。您可能還想要打印數據文件行號'$ .'。通過這種方式,您可以在整個文件中查看該陣列的大小,並瞭解問題的嚴重程度。 – Borodin

0

OP,你可以用很短的測試腳本重複的問題?也許你可以發佈測試腳本,我會在我的機器上運行它。也許你沒有寫的一個模塊會變得詭異。


試試這個代碼與散列。

use strict; 
use warnings; 

my ($xx,$yy); # Coordinate variables 
my (@elems); # Array of elements to be stored on each matrix position 

# Generate an array of 3000 floating point values. 
# The list will be added to each array element. In the true script 
# of course, each element has a list of completely different values. 
# Here I use the same list of values for simplicity. 
for ($xx = 1; $xx < 3000; $xx++) { 
    push(@elems, 1+$xx/10000); 
} 

# Fill in each matrix element with the generated array 
my %psStored; 
my $s; 
$s=join('=',@elems); 
for ($xx = 0; $xx < 300; $xx++) { 
    print "Row [$xx]\n"; 
    for ($yy = 0; $yy < 300; $yy++) { 
    $psStored{$xx,$yy} = $s; # Here I join all elements and store them as a string. 
    #push(@{$psStored[$xx][$yy]}, @elems); 
    } 
} 
+0

我編輯了這篇文章,並添加了一個簡短的代碼,給我內存不足的錯誤。正如你所看到的,我只是填滿了記憶。該程序在「行[231]」後出現錯誤。 – Andrea

+0

幹得好。打印「行[139]」後,我也發現內存不足錯誤。我在使用Perl 5.8.8的Linux RHEL 5.5.56上。 – Bulrush

+0

這很有趣!所以有一個問題,不是嗎? – Andrea