2012-05-15 3 views
3

저는 Perl로 구현 된 프로젝트에서 작업 중이며 스레드를 사용하여 작업을 분산시키는 것이 좋을 것이라고 생각했습니다. 작업이 서로 독립적으로 수행 될 수 있고 독서 만 가능하기 때문입니다 메모리의 공유 데이터에서 그러나 성능은 내가 예상 한대로 아무데도 없습니다. 그래서 약간의 조사가 끝나면 Perl의 쓰레드는 기본적으로 빠르다는 결론을 내릴 수 있습니다. 그러나 하나의 공유 변수를 구현하자마자 성능이 저하 될까 궁금합니다.공유 변수가있는 Perl 스레드 성능

예를 들어,이 작은 프로그램은 아무것도 공유가 없으며 (예상대로)는 CPU의 75 %를 소모 :

use threads; 

sub fib { 
    my ($n) = @_; 
    if ($n < 2) { 
    return $n; 
    } else { 
    return fib($n - 1) + fib($n - 2); 
    } 
} 

my $thr1 = threads->create('fib', 35); 
my $thr2 = threads->create('fib', 35); 
my $thr3 = threads->create('fib', 35); 

$thr1->join; 
$thr2->join; 
$thr3->join; 

그리고를 최대한 빨리 공유 변수 $a 소개로, CPU 사용은 40 사이 어딘가에 %와 50 % :

use threads; 
use threads::shared; 

my $a : shared; 
$a = 1000; 

sub fib { 
    my ($n) = @_; 
    if ($n < 2) { 
    return $n; 
    } else { 
    return $a + fib($n - 1) + fib($n - 2); # <-- $a was added here 
    } 
} 

my $thr1 = threads->create('fib', 35); 
my $thr2 = threads->create('fib', 35); 
my $thr3 = threads->create('fib', 35); 

$thr1->join; 
$thr2->join; 
$thr3->join; 

그래서 $a는 읽기 전용이며 어떤 잠금이 발생하지 않으며, 아직 성능이 감소합니다. 왜 이렇게되는지 궁금합니다.

현재 Windows XP의 Cygwin에서 Perl 5.10.1을 사용하고 있습니다. 불행히도 필자는 (희망적으로) 최근의 Perl을 사용하여 Windows가 아닌 머신에서 이것을 테스트 할 수 없었다.

+0

잠금이 수행됩니다. 각 쓰레드는 다른 쓰레드가'$ a'를 동시에 읽으려고하는지 알지 못하기 때문에 구현은'$ a'를 읽어 들여야 만합니다. 본질적으로 테스트 코드는 잠금 장치 주위의 단단한 루프입니다. –

+0

하지만'$ a'가 수정되지 않았다는 것을 알고 있습니다. 잠금이 필요 없다는 힌트를 추가 할 방법이 없습니까? –

+0

'$ a' 공유를 표시하지 마십시오. 변수'shared'를 표시하는 것은 특히 변경 사항이 스레드간에 동기화되어야 함을 나타냅니다. 더 좋은 것은'$ b'라는 변수를 공유하지 않고'$ a = $ b; '라는 변수를 만들고 각 스레드가'$ b'를 올바른 값으로 설정하는 것입니다. 그렇게하면 각 스레드는 공유 변수를 한 번만 읽습니다. –

답변

3

코드는 동기화 된 구조 주위의 단단한 루프입니다. 각 스레드가 공유 변수를 각 스레드마다 한 번만 공유하여 공유되지 않는 변수에 복사하도록하여 최적화하십시오.

+0

좋습니다. 고맙습니다. 데이터 복사는 나를 위해 확장되지 않으므로 스레드를 사용하는 것은 결국 이동하는 방법이 아닙니다. –

+3

동기화 된 구조 주위에 단단한 루프를 피할 수 없다면 스레드가 이동하는 방법이 아닐 수도 있습니다. –

+3

@BramSchoenmakers : 붙여 넣은 예제 코드는 실제 코드가 아닙니다. 왜냐하면이 간단한 경우에는 쉽게 공유를 피할 수 있기 때문입니다. 그러나 극단적 인 성능 손실은 코드의 "장난감"성격에 기인합니다. 단일 공유 변수를 둘러싼 그런 엄격한 루프입니다. 실제 코드에서는 공유를 피하기 위해 복사를 사용하는 것이 훨씬 더 어려울 수 있지만 루핑은 단일 공유 변수 주위가 아니라 여러 다른 변수 주위에서 이루어지지 않을 것입니다. 따라서 실제 코드에서 이러한 극단적 인 성능 손실을 보지 못할 것입니다. –

0

Perl에서 많은 양의 데이터를 포함하는 공유 객체를 구성 할 수 있으며 여분의 복사본은 걱정하지 않아도됩니다. 스레드를 사용하는지 여부에 따라 공유 데이터가 개별 스레드 또는 프로세스 내부에 있기 때문에 작업자를 생성 할 때 성능에 영향을 미치지 않습니다.

use MCE::Hobo; # use threads okay or parallel module of your choice 
use MCE::Shared; 

# The module option constructs the object under the shared-manager. 
# There's no trace of data inside the main process. The construction 
# returns a shared reference containing an id and class name. 

my $data = MCE::Shared->share({ module => 'My::Data' }); 
my $b; 

sub fib { 
    my ($n) = @_; 
    if ($n < 2) { 
    return $n; 
    } else { 
    return $b + fib($n - 1) + fib($n - 2); 
    } 
} 

my @thrs; 

push @thrs, MCE::Hobo->create(sub { $b = $data->get_keys(1000), fib(35) }); 
push @thrs, MCE::Hobo->create(sub { $b = $data->get_keys(2000), fib(35) }); 
push @thrs, MCE::Hobo->create(sub { $b = $data->get_keys(3000), fib(35) }); 

$_->join() for @thrs; 

exit; 

# Populate $self with data. When shared, the data resides under the 
# shared-manager thread (via threads->create) or process (via fork). 

package My::Data; 

sub new { 
    my $class = shift; 
    my %self; 

    %self = map { $_ => $_ } 1000 .. 5000; 

    bless \%self, $class; 
} 

# Add any getter methods to suit the application. Supporting multiple 
# keys helps reduce the number of trips via IPC. Serialization is 
# handled automatically if getter method were to return a hash ref. 
# MCE::Shared will use Serial::{Encode,Decode} if available - faster. 

sub get_keys { 
    my $self = shift; 
    if (wantarray) { 
    return map { $_ => $self->{$_} } @_; 
    } else { 
    return $self->{$_[0]}; 
    } 
} 

1;