2013-03-19 13 views
7

Poszukuję informacji o tym, jak działa funkcja Perla grep. Robię to:Czy sortowanie pomaga w wydajności grep w Perl

if (grep{ $foo == $_ } @bar) { 
    some code; 
} 

Załóżmy @bar jest duża (setki tysięcy elementów). Z moimi danymi, jeśli sortuję @bar, wartości $foo są bardziej prawdopodobne w pobliżu początku tablicy niż przy końcu. Zastanawiam się, czy to pomoże w osiągach.

Innymi słowy, z powyższym kodem, grep przesuwa się sekwencyjnie przez @bar sprawdzając, czy $foo == $_, a następnie natychmiast kończy działanie, gdy jakakolwiek wartość zostanie uznana za prawdziwą? A może faktycznie sprawdziłby każdy element @bar przed zwróceniem wartości?

+1

dobre pytanie.Myślę, że nie używaj 'grep', ale' for() 'do twojego wcześniejszego wyjścia – gaussblurinc

+4

I zobacz mój komentarz poniżej. 'List :: MoreUtils' na CPAN może robić, co chcesz, jeśli dobrze rozumiem twoje zadanie – gaussblurinc

+0

@loldop Powinieneś umieścić to jako odpowiedź. Wygląda na to, że 'firstidx' zrobi to, co chcę. – itzy

Odpowiedz

10

grep nie powoduje zwarcia, więc zamawianie elementów nie ma znaczenia.

Podczas gdy lista :: MoreUtils first ma zwarcie, cała lista musi zostać umieszczona na stosie zanim zostanie wywołana.

To będzie najlepsze:

for (@bar) { 
    if ($foo == $_) { 
     some code; 
     last; 
    } 
} 

Updated: I początkowo potwierdził ciągu indeksów ponieważ używa O (1) pamięć, ale tak nie for (@bar) (w przeciwieństwie do for (LIST) w ogóle) jako ysth przypomnienia mnie.

+1

Czy na pewno używanie indeksów będzie szybsze niż bezpośrednie korzystanie z elementów? – TLP

+0

@TLP, Nie umieszcza tablicy na stosie. Pamięć mądra, tak. Prędkość, może odrobinę wolniejsza od pojedynczego dodatkowego op. – ikegami

+2

for na tablicy nie umieszcza tablicy na stosie – ysth

6

Ponieważ twoje użycie grep jest w kontekście skalarnym, zwraca liczbę pasujących elementów. Aby to obliczyć, Perl musi odwiedzić każdy element w każdym razie, więc jest mało prawdopodobne, że sortowanie pomogłoby w osiągnięciu wydajności pod tym kątem.

+0

Może [List :: MoreUtils] (https://metacpan.org/module/List::MoreUtils) może pomóc w tej sytuacji? Nie jestem pewien – gaussblurinc

+0

@loldop Myślę, że tak! Nie jestem szczególnie zaznajomiony z tym modułem, ale wygląda na to, że jest bardzo dobrze skonstruowany i List :: MoreUtils :: any byłby przydatny tutaj. – sidyll

+3

A może first_index ... – squiguy

1

W odniesieniu do Twojego pytania

z moimi danymi, jeśli rodzaj @bar, wartości $ foo jest bardziej prawdopodobne, aby pojawić się w pobliżu początku tablicy niż pod koniec. Zastanawiam się, czy to pomoże w osiągach.

Jeżeli lista jest sortowana w kolejności numerycznej następnie można napisać

sub contains { 
    my ($list, $item) = @_; 
    for (@$item) { 
    return $_ == $item if $_ >= $item; 
    } 
    return !1; 
} 

some_code() if contains(\@bar, $foo); 
+1

Jeśli lista jest posortowana, możesz użyć bsearch i znaleźć wynik szybciej. Ponadto, 'return; zwraca pustą listę lub undef, podczas gdy' return $ _ == $ item' zwraca 1 lub "", więc ta sekcja ma 4 różne zwracane wartości, to jest złe. – PSIAlt

+0

Jeśli lista jest posortowana, możesz użyć wyszukiwania binarnego. – ikegami

+0

@ikegami: Tak, to prawda. PSIAlt również uważał, że musi to wskazać. OP zapytał, czy posortowanie danych poprawiłoby wydajność 'grep', a moja odpowiedź brzmi:" Nie, ale pomogłoby to w przypadku takiego odpowiednika kodowanego ręcznie (i wielu innych) "* – Borodin

2

W przykładzie grep będzie iteracyjne cały szereg niezależnie ile elementy dopasowane.

Jeśli możesz uporządkować tę tablicę - szybciej będzie wyszukiwać wartości za pomocą wyszukiwania binarnego. Możesz także przekonwertować tablicę na hasz (za pomocą keys = element tablicy) i wykonywać kontrole ze stałym czasem, ale zajmie to dodatkową pamięć.

0

To zależy. A grep { $x == $_ } @a nie korzysta z przewidywania rozgałęzień, ale robi to grep { $x < $_ } @a!

#!/usr/bin/env perl 

use strict; 
use warnings; 

use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC); 

use constant MIN => 0; 
use constant MAX => 1000; 
use constant AVG => int(MIN + (MAX - MIN)/2); 
use constant N_LOOPS => 40000; 
use constant ARRAY_LEN => 10000; 

## is grep faster for sorted arrays? 

## 
## RANDOM ARRAY VALUES 
## 
my $n = 0; 
my @a = map { int(rand() * (MAX - MIN) + MIN) } 1 .. ARRAY_LEN; 
my $duration = -clock_gettime (CLOCK_MONOTONIC); 
for(my $i = 0; $i < N_LOOPS; $i++) { 
    $n += grep { AVG < $_ } @a; 
} 
$duration += clock_gettime (CLOCK_MONOTONIC); 
print "duration: $duration secs, n = $n".$/; 

## 
## PREDICTABLE ARRAY VALUES 
## 
$n = 0; 
@a = sort {$a <=> $b} @a; 
$duration = -clock_gettime (CLOCK_MONOTONIC); 
for(my $i = 0; $i < N_LOOPS; $i++) { 
    $n += grep { AVG < $_ } @a; 
} 
$duration += clock_gettime (CLOCK_MONOTONIC); 
print "duration: $duration secs, n = $n".$/; 

## and now we try to eliminate side effects by repeating 

## 
## RANDOM ARRAY VALUES 
## 
$n = 0; 
@a = map { int(rand() * (MAX - MIN) + MIN) } 1 .. ARRAY_LEN; 
$duration = -clock_gettime (CLOCK_MONOTONIC); 
for(my $i = 0; $i < N_LOOPS; $i++) { 
    $n += grep { AVG < $_ } @a; 
} 
$duration += clock_gettime (CLOCK_MONOTONIC); 
print "duration: $duration secs, n = $n".$/; 

## 
## PREDICTABLE ARRAY VALUES 
## 
$n = 0; 
@a = sort {$a <=> $b} @a; 
$duration = -clock_gettime (CLOCK_MONOTONIC); 
for(my $i = 0; $i < N_LOOPS; $i++) { 
    $n += grep { AVG < $_ } @a; 
} 
$duration += clock_gettime (CLOCK_MONOTONIC); 
print "duration: $duration secs, n = $n".$/; 

Wyniki:

duration: 27.7465513650095 secs, n = 199880000 <-- unsorted 
duration: 26.129752348992 secs, n = 199880000 <-- sorted 
duration: 28.3962040760089 secs, n = 202920000 <-- unsorted 
duration: 26.082420132996 secs, n = 202920000 <-- sorted 

Zobacz także Why is it faster to process a sorted array than an unsorted array?

+0

Jak zauważa ikegami, 'grep' przejdzie przez każdy element na liście. Jest niezależny od warunkowego. – Zaid

+0

Nie. Pierwszy argument do grep decyduje o tym, która strona gałęzi * wewnątrz grep * zostanie pobrana: dodaj element do listy wyników lub nie. – user1050755