Jak emulować wc -l w Raku

9

W Perlu 5 możesz emulować wc -lza pomocą oneliner:

perl -lnE 'END {say $.}' test.txt

Jak wdrożyć tę funkcjonalność w Raku

Jeśli spróbujesz to zaimplementować:

raku -e 'say "test.txt".IO.open.lines.elems'

okazuje się powolny i zużywa dużo pamięci

Informacje do odtworzenia:

$ wget http://eforexcel.com/wp/wp-content/uploads/2017/07/1500000%20Sales%20Records.zip
$ unzip "1500000 Sales Records.zip"
$ mv "1500000 Sales Records.csv" part.txt
$ for i in `seq 1 10`; do cat part.txt >> test.txt ; done
$ du -sh test.txt
1.8G    test.txt

$ time wc -l test.txt
15000000 test.txt

real    0m0,350s
user    0m0,143s
sys     0m0,205s

$ time perl -lnE 'END { say $. }' test.txt
15000001

real    0m1,981s
user    0m1,719s
sys     0m0,256s

$ time raku -e 'say "test.txt".IO.open.lines.elems'
15000001

real    2m51,852s
user    0m25,129s
sys     0m6,378s

# Using swap (maximum uses 2.2G swap):
# Before `raku -e ''`

$ free -m
              total        used        free      shared  buff/cache   available
Mem:          15009        1695       12604         107         708       12917
Swap:          7583           0        7583

# After `raku -e ''`

$ free -m
              total        used        free      shared  buff/cache   available
Mem:          15009         752       13923          72         332       13899
Swap:          7583         779        6804

# Swap not used
$ time raku -ne '++$ andthen END .say' test.txt
15000001

real    1m44,906s
user    2m14,165s
sys     0m0,653s

$ raku -v
This is Rakudo version 2019.11 built on MoarVM version 2019.11
implementing Perl 6.d.
Sportowiec
źródło
3
Czy możesz dodać informacje o czasie i rzeczywiste dane wyjściowe wc(które powinny obejmować rozmiar pliku). Dziękuję Ci.
Elizabeth Mattijsen
Zaktualizuj pytanie - dodano przykład reprodukcji
TheAthlete
2
@TheAthlete Dziękujemy za dodane informacje. Myślę, że prawdopodobnie będzie to historycznie interesujące [raku] SO z powodu prawdopodobnego stałego zmniejszania się różnic w wydajności w Rakudo w nadchodzących latach, więc proszę rozważyć dodanie kilku dodatkowych informacji. Po pierwsze, chociaż jasne jest, że używasz ostatniego rakudo (ponieważ piszesz raku), nadal dobrze byłoby mieć raku -vwyjście. Rozważ także dodanie wyniku z mojej obecnej sugestii. Mogę też zastanowić się nad przejściem na 'ascii'dekoder później w ten weekend, aby uzyskać lepszy czas.
raiph
1
Na marginesie: -lflaga dla perla znacznie spowalnia perla i w tym przypadku nie jest przydatna. Na mojej maszynie dla pliku o losowej długości linii i około 200 000 linii, usunięcie -lskutkuje 40% poprawą.
Sorin

Odpowiedzi:

8

Jedna opcja, która prawdopodobnie będzie dość powolna w porównaniu do, perlale warta porównania:

raku -ne '++$ andthen END .say' test.txt

Opcja lwiersza poleceń jest zbędna.

$ jest anonimowym skalarem stanu.

andthensprawdza, czy jego lhs jest zdefiniowane, a jeśli tak, ustawia tę wartość jako topic ( $_), a następnie ocenia jej rhs.

ENDjest podobny do perl's END. Zauważ, że powraca Nildo tego, andthenale to nie ma znaczenia, ponieważ używamy ENDinstrukcji dla jej efektu ubocznego.

Kilka rzeczy wpłynie na szybkość tego kodu. Niektóre rzeczy, o których mogę myśleć:

  • Narzut związany z uruchomieniem kompilatora. Ignorując wszystkie używane moduły, rakukompilator Rakudo ma narzut typowy sprzęt około około jednej dziesiątej sekundy w porównaniu do dość pomijalnego perl.

  • Pojęcie „linii”. W perldomyślnym pojęcie przetwarzania linii czyta serię bajtów, z których część stanowią zakończenie linii. W rakudomyślnym pojęciem przetwarzania linii jest odczytywanie łańcucha UTF-8, z których niektóre reprezentują końce linii. Zatem perlpowoduje jedynie obciążenie odczytu dekodera ASCII (lub rozszerzonego ASCII), podczas gdy rakupowoduje obciążenie odczytu dekodera UTF-8.

  • Optymalizacje kompilatora. perljest ogólnie zoptymalizowany do maksimum. Nie zaskoczyłoby mnie, gdyby perl -lnE 'END {say $.}' test.txtskorzystał z pewnych sprytnych optymalizacji. W przeciwieństwie do tego, prace nad optymalizacją Rakudo wciąż są stosunkowo wczesne.

Jedyne, co myślę, że każdy może zrobić w odniesieniu do pierwszego i ostatniego z trzech punktów, o których wspomniałem powyżej, to czekać N lat i / lub przyczyniać się do poprawy kompilatora.

Domyślnie będzie sposób obejścia UTF-8 Raku. Być może coś podobnego jest już wykonalne i znacznie szybsze niż domyślne Raku, przynajmniej ignorując narzut związany z użyciem modułu o nazwie foo:

raku -Mfoo -ne '++$ andthen END .say' test.txt

gdzie moduł fooprzełącza domyślne kodowanie plików I / O na ASCII lub cokolwiek z dostępnych kodowań .

Nie sprawdziłem, czy jest to wykonalne w obecnym Rakudo, ale byłbym zaskoczony, gdyby nie było.

raiph
źródło