2010-06-04 27 views
7

Linux, bir çatalın ardından bellek kullanımını düşük tutmak için COW kullanır, ancak Perl 5 değişkenlerinin perl numaralı çalışmasında bu optimizasyonu ortadan kaldırdığı görülmektedir. Örneğin, değişken için:Alt süreçler meta verilere dokunduğunda bellek patlamasını nasıl engelleyebilirim?

my $s = "1"; 

perl gerçekten depoluyor:

SV = PVIV(0x100821610) at 0x1008272e8 
    REFCNT = 1 
    FLAGS = (IOK,POK,pIOK,pPOK) 
    IV = 1 
    PV = 0x100201d50 "1"\0 
    CUR = 1 
    LEN = 16 
: Sayısal bir bağlamda o dizeyi kullandığınızda

SV = PV(0x100801068) at 0x1008272e8 
    REFCNT = 1 
    FLAGS = (POK,pPOK) 
    PV = 0x100201d50 "1"\0 
    CUR = 1 
    LEN = 16 

, verileri temsil eden C struct değiştirir

Dize işaretçisinin kendisi değişmedi (hala 0x100201d50), ancak şimdi farklı bir C struct () PV yerine). Değeri hiç değiştirmedim, ama aniden bir COW maliyeti ödüyorum. Bu zaman tasarrufu (perl"0"0 bir ikinci kez dönüştürmek zorunda kalmazsınız böylece bir Perl 5 değişkeninin perl gösterimini kilitlemek için herhangi bir yolu var) hack benim bellek kullanımı zarar vermez?

perl -MDevel::Peek -e '$s = "1"; Dump $s; $s + 0; Dump $s' 

cevap

4

Ben şimdiye kadar bulduk tek çözüm, ben üst süreçte bekliyoruz dönüşümlerin hepsi yapmak perl zorlamak emin olmaktır:

Not temsilcilikleri bu koddan üretildi yukarıda . Ve aşağıdaki koddan görebilirsiniz, hatta sadece biraz yardımcı olur.

Sonuçlar:

Useless use of addition (+) in void context at z.pl line 34. 
Useless use of addition (+) in void context at z.pl line 45. 
Useless use of addition (+) in void context at z.pl line 51. 
before eating memory 
used memory: 71 
after eating memory 
used memory: 119 
after 100 forks that don't reference variable 
used memory: 144 
after children are reaped 
used memory: 93 
after 100 forks that touch the variables metadata 
used memory: 707 
after children are reaped 
used memory: 93 
after parent has updated the metadata 
used memory: 109 
after 100 forks that touch the variables metadata 
used memory: 443 
after children are reaped 
used memory: 109 

Kod: Eğer açılışta ve ömür boyu SON aşaması unuttum olmamalıdır çalışması sırasında COW kaçınmak Neyse

#!/usr/bin/perl 

use strict; 
use warnings; 

use Parallel::ForkManager; 

sub print_mem { 
    print @_, "used memory: ", `free -m` =~ m{cache:\s+([0-9]+)}s, "\n"; 
} 

print_mem("before eating memory\n"); 

my @big = ("1") x (1_024 * 1024); 

my $pm = Parallel::ForkManager->new(100); 

print_mem("after eating memory\n"); 

for (1 .. 100) { 
    next if $pm->start; 
    sleep 2; 
    $pm->finish; 
} 

print_mem("after 100 forks that don't reference variable\n"); 

$pm->wait_all_children; 

print_mem("after children are reaped\n"); 

for (1 .. 100) { 
    next if $pm->start; 
    $_ + 0 for @big; #force an update to the metadata 
    sleep 2; 
    $pm->finish; 
} 

print_mem("after 100 forks that touch the variables metadata\n"); 

$pm->wait_all_children; 

print_mem("after children are reaped\n"); 

$_ + 0 for @big; #force an update to the metadata 

print_mem("after parent has updated the metadata\n"); 

for (1 .. 100) { 
    next if $pm->start; 
    $_ + 0 for @big; #force an update to the metadata 
    sleep 2; 
    $pm->finish; 
} 

print_mem("after 100 forks that touch the variables metadata\n"); 

$pm->wait_all_children; 

print_mem("after children are reaped\n"); 
+0

Güzel, ama birkaç yüz MB veri ayırdığınızda, birkaç çocuğu çatalladığınızda ve çocukların bitmesiyle ne olacağını biliyor musunuz? GC yine de seni öldürecek. Bu üzücü bir hikaye ama Perl bu tür bir iş için sadece yanlış bir araçtır.Kısmen END {kill 9 $$} yaklaşımını kullanarak çözdük ancak bu noktada daha iyi bir araç aramalıyız ;-) –

+1

GC beni rahatsız etmiyor, gerçek kod "mod_perl" tabanlı ve her çocuk birçok kez yeniden kullanılıyor . Problem şu ki, ebeveynin içinde yüklenen konfigürasyon verileri, çocukların hiçbir zaman modifiye etmemesine rağmen muhtemelen yüzlerce çocuğun her birine kopyalanıyor (Perl 5'in bakış açısına göre, 'perl' meta verilerle uğraşıyor). Göz önünde bulundurduğum diğer çözüm, yapılandırma verilerini ayrı bir sürece taşımak ve çocukların alan adı soketleriyle konuşmasına izin vermek. –

+0

Daha hızlı olacak paylaşılan belleği de kullanabilirsiniz. –

2

. Kapanışta, ilk olarak ref sayım güncellemeleri olduğunda iki GC aşaması vardır, böylece sizi güzel bir şekilde öldürebilir.

END { kill 9, $$ } 
+0

Cevabınız çok anlamlı değil. – Ether

+1

@Programın sonunda tüm değişkenler "REFCNT" alanlarını GC tarafından azaltılır. Bu, tüm değişkenler çocuk işlemine çekildiğinden, bellek kullanımında ani bir artışa neden olacaktır. Önerdiği kod, çocuğun çöp toplama aşaması başlamadan önce ölmesine neden olacaktır. –

2

Bu söylemeye gerek yok, ama İNEK başına yapı temelinde olmaz, ancak bir bellek sayfası bazında: Sen o çirkin çözebilir. Bu nedenle, tüm bellek sayfasındaki bir şeyin kopyalama bedelini ödemeniz için bunun gibi değiştirilmesi yeterlidir.

Linux üzerinde böyle sayfa boyutunu sorgulayabilir: 4096 bayt olması benim sistemde

getconf PAGESIZE 

. Bu alanda çok sayıda Perl skalar yapıyı sığdırabilirsiniz. Bu şeylerden biri modifiye edilirse, Linux tüm şeyi kopyalamak zorunda kalacak.

Genel olarak bellek arenasının kullanılması iyi bir fikirdir. Değişken ve değişmez verilerinizi birbirinden ayırabilmeniz için, değişebilir veriler ile aynı bellek sayfasında bulunması nedeniyle değişmez veriler için COW maliyetleri ödemek zorunda kalmayacaksınız.

+1

Sorun, umurumda olan verilerden hiçbirini değiştirmese de 'struct'leri' struct 'güncellemesidir, bu yüzden değişmez verilerimi ayıramam (çünkü değişmez veriler yoktur). –

+1

Ayrıca, değişkenleri değişmez verilerden ayırmak için kolay bir yol yoktur (eğer varsa bile). – Ether

İlgili konular