2013-08-13 12 views
5

Ho un lungo script perl che memorizza alcune informazioni da un file in un hash, e ogni tanto (qui, ogni 100000 posizioni), stampa i valori dell'hash per quella finestra, quindi tenta per eliminare la maggior parte dei contenuti dall'hash, ad eccezione di un piccolo buffer da utilizzare nella successiva iterazione.memoria perl gonfia in hash anche quando si esegue l'eliminazione?

Dico che tenta di eliminare il contenuto, perché il mio script fa esplodere in memoria, fino a quando non utilizza tutta la memoria e si blocca. Anche se è sembra che l'istruzione di cancellazione riduca il numero di chiavi nell'hash (vedi STDERR di stampa di seguito) solo su un numero ridotto di elementi, il consumo di memoria dello script è pari a come se non stia cancellando il contenuto di . Se commento l'istruzione delete, utilizza la stessa quantità di memoria dello , con l'unica differenza che richiede più tempo per iterare . Sembra che il numero di chiavi sia ridotto dopo il comando di cancellazione , ma non il numero di valori.

Mi sono assicurato che non ci fosse buffering strano con la lettura e l'emissione di risultati. In effetti, lo script non esaurisce la memoria se Mi limito a commentare i luoghi in cui viene utilizzato% hash, quindi l'ho ristretto a fino al riempimento e all'eliminazione delle voci in% hash.

Ho anche provato a utilizzare un hashref anziché% hash e lo stesso è ancora .

Come mai si sta facendo esplodere nella memoria? Mi manca qualcosa di ovvio qui?

my %hash; 
# while (Read from input) { 
# Fill hash here and there with: $hash{$this_p}{$this_c}++ 
# ... 
# Then every 100000 entries 
if (not $pos % 100000) { 
    print STDERR "pre ", scalar %hash , "\n"; 
warn total_size(\%hash); 
    for my $p (sort { $a <=> $b } keys %hash) { 
     last if ($p > $max_possible{$subset}); 
     if ($p + $buffer < $pos) { 
      print $out "$p\t"; 
      for my $c (keys %{ $hash{$p} }) { 
       print $out "$c ". $hash{$p}{$c} . ";"; 
      } 
      print $out "\n"; 
      delete $hash{$p}; 
     } 
    } 
    print STDERR "post ", scalar %hash , "\n"; 
warn total_size(\%hash); 
    } 
#} 

uscita è qualcosa di simile:

pre 322484/524288 
134297952 at /home/ 
post 681/524288 
4368924 at /home/av 
pre 681/524288 
4368924 at /home/av 
post 681/524288 
4368924 at /home/av 
pre 681/524288 
4368924 at /home/av 
post 681/524288 
4368924 at /home/av 
pre 629257/1048576 
260016542 at /home/ 
post 344/1048576 
8477509 at /home/av 
pre 1903885/4194304 
689633878 at /home/ 
post 900/4194304 
33790436 at /home/a 
[...] 

Questo sta usando v5.14.2 perl su una scatola a 64 bit di Linux.

+0

Quando si eliminano elementi di un hash, sembra che i bucket allocati non vengano ripristinati. 'perl -le '$ min = 0; $ max = maiusc; % a = mappa $ _ => 1, $ min .. $ max; per (1 .. $ max - 10) {cancella $ a {$ _}}; print scalar% a '1000001' Output: '5/524288' Prova a copiare la chiave/i valori. – TLP

+0

@TLP copia la chiave/i valori? – 719016

+0

crea un nuovo hash copiando quello vecchio e lascia che il vecchio esca dallo scope – mirod

risposta

6

Il numero di elementi inseriti nell'hash in ciascun passaggio aumenta mentre il programma viene eseguito. 0+keys(%hash) direbbe il numero esatto, ma il numeratore nella seguente sarà simile (ma più basso)

     322484 added 
pre 322484/524288 
         321803 cleared (99.8% of added) 
post 681/524288 
          0 added 
pre  681/524288 
          0 cleared (100.0% of added) 
post 681/524288 
          0 added 
pre  681/524288 
          0 cleared (100.0% of added) 
post 681/524288 
         628576 added 
pre 629257/1048576 
         628913 cleared (100.0% of added) 
post 344/1048576 
        1903541 added 
pre 1903885/4194304 
        1902641 cleared (100.0% of added) 
post 900/4194304 

Il denominatore cresce solo perché il numeratore è in crescita. Non è rilevante. Non è una crescita cumulativa. Diventerebbe così grande anche se avessi un hash fresco ogni volta.

Il numeratore cresce solo perché il numero di elementi aggiunti all'hash aumenta. Come puoi vedere, il codice di cancellazione funziona molto bene.

Questo non sembra affatto una perdita di memoria; sembra che tu stia effettivamente usando la memoria. Forse dovresti eliminarlo più spesso?

Invece di

if (not $pos % 100000) { 
    ... 
} 

uso

if (keys(%hash) >= 1_000_000) { 
    ... 
} 

O se volete un feedback regolare,

if (++$since_last >= 100_000 || keys(%hash) >= 1_000_000) { 
    $since_last = 0; 
    ... 
} 

regolare i limiti come richiesto.

Problemi correlati