W bash
skrypcie potrzebuję różnych wartości z /proc/
plików. Do tej pory mam kilkadziesiąt wierszy bezpośrednio w plikach:
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
Aby uczynić to bardziej wydajnym, zapisałem zawartość pliku w zmiennej i grep, że:
a=$(</proc/meminfo)
echo "$a" | grep -oP '^MemFree: *\K[0-9]+'
Zamiast otwierać plik wiele razy, powinno to po prostu otworzyć go raz i grepować zawartość zmiennej, co, jak zakładam, będzie szybsze - ale w rzeczywistości jest wolniejsze:
bash 4.4.19 $ time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null
real 0m0.803s
user 0m0.619s
sys 0m0.232s
bash 4.4.19 $ a=$(</proc/meminfo)
bash 4.4.19 $ time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null
real 0m1.182s
user 0m1.425s
sys 0m0.506s
To samo dotyczy dash
i zsh
. Podejrzewałem specjalny stan /proc/
plików jako przyczynę, ale kiedy kopiuję zawartość /proc/meminfo
do zwykłego pliku i używam, że wyniki są takie same:
bash 4.4.19 $ cat </proc/meminfo >meminfo
bash 4.4.19 $ time for i in $(seq 1 1000);do grep ^MemFree meminfo; done >/dev/null
real 0m0.790s
user 0m0.608s
sys 0m0.227s
Użycie ciągu tutaj do zapisania potoku sprawia, że jest on nieco szybszy, ale nadal nie tak szybki jak w przypadku plików:
bash 4.4.19 $ time for i in $(seq 1 1000);do <<<"$a" grep ^MemFree; done >/dev/null
real 0m0.977s
user 0m0.758s
sys 0m0.268s
Dlaczego otwieranie pliku jest szybsze niż czytanie tej samej treści ze zmiennej?
/proc/
plików jako powód, ale kiedy skopiować zawartość/proc/meminfo
do pliku regularnego i stosowania, że wyniki są takie same:” To nie specjalny do/proc/
plików, czytanie zwykłych plików jest również szybsze!Odpowiedzi:
Tutaj nie chodzi o otwarcie pliku, a nie o odczytanie zawartości zmiennej, ale o wymyślenie dodatkowego procesu lub nie.
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
rozwidla proces, który wykonujegrep
, który otwiera/proc/meminfo
(plik wirtualny, w pamięci, brak dysku I / O zaangażowany) odczytuje ją i pasuje do wyrażenia regularnego.Najdroższą częścią jest rozwidlenie procesu i załadowanie narzędzia grep i jego zależności od biblioteki, wykonanie dynamicznego łączenia, otwarcie bazy danych ustawień regionalnych, dziesiątek plików na dysku (ale prawdopodobnie buforowanych w pamięci).
Część dotycząca czytania
/proc/meminfo
jest nieznaczna w porównaniu z tym, że jądro potrzebuje niewiele czasu na wygenerowanie informacji igrep
potrzebuje czasu na ich odczytanie.Jeśli się
strace -c
na tym uruchomisz , zobaczysz, że jedenopen()
i jedenread()
wywołania systemowe używane do odczytu/proc/meminfo
to orzeszki ziemne w porównaniu do wszystkiego, cogrep
robi na początku (strace -c
nie liczy forkingu).W:
W większości powłok obsługujących tego
$(<...)
operatora ksh powłoka po prostu otwiera plik i odczytuje jego zawartość (i usuwa końcowe znaki nowego wiersza).bash
jest inny i znacznie mniej wydajny, ponieważ powoduje, że proces dokonuje odczytu i przekazuje dane do elementu nadrzędnego za pomocą potoku. Ale tutaj zrobiono to raz, więc to nie ma znaczenia.W:
Powłoka musi odrodzić dwa procesy, które działają jednocześnie, ale współdziałają ze sobą za pomocą potoku. Tworzenie fajki, burzenie, pisanie i czytanie z niej ma niewielki koszt. Znacznie większy koszt to pojawienie się dodatkowego procesu. Planowanie procesów również ma pewien wpływ.
Może się okazać, że użycie
<<<
operatora zsh sprawia, że jest to nieco szybsze:W Zsh i Bash odbywa się to poprzez zapisanie zawartości
$a
pliku tymczasowego, który jest tańszy niż tworzenie dodatkowego procesu, ale prawdopodobnie nie przyniesie żadnych korzyści w porównaniu z natychmiastowym pobraniem danych/proc/meminfo
. Jest to nadal mniej wydajne niż podejście, które kopiuje się/proc/meminfo
na dysk, ponieważ zapisywanie pliku tymczasowego odbywa się przy każdej iteracji.dash
nie obsługuje ciągów tutaj, ale jego heredoki są implementowane za pomocą potoku, który nie wymaga odrodzenia dodatkowego procesu. W:Powłoka tworzy potok, rozwidla proces. Dziecko wykonuje
grep
ze swoim stdin jako końcem odczytu potoku, a rodzic zapisuje zawartość na drugim końcu potoku.Ale obsługa rur i synchronizacja procesów nadal prawdopodobnie będzie droższa niż zwykłe uzyskiwanie danych
/proc/meminfo
.Treść
/proc/meminfo
jest krótka i zajmuje niewiele czasu. Jeśli chcesz zapisać niektóre cykle procesora, chcesz usunąć kosztowne części: rozwidlanie procesów i uruchamianie zewnętrznych poleceń.Lubić:
Unikaj
bash
jednak tego, czy dopasowanie wzorca jest bardzo nieskuteczne. Za pomocązsh -o extendedglob
możesz skrócić go do:Zauważ, że
^
jest wyjątkowy w wielu powłokach (Bourne, fish, rc, es i zsh przynajmniej z opcją Extendedglob), polecam zacytowanie go. Zauważ też, żeecho
nie można go użyć do wyprowadzenia dowolnych danych (stąd moje użycieprintf
powyżej).źródło
printf
mówisz powłoka musi tarło dwa procesy, ale nie jestprintf
wbudowanym poleceniem powłoki?grep
i wykonuje.A | B
są pewne powłoki, takie jak AT&T ksh lub zsh, które działająB
w bieżącym procesie powłoki, jeśli jest to polecenie wbudowane, złożone lub funkcyjne, nie znam żadnej, która działałabyA
w bieżącym procesie. Aby to zrobić, musieliby obsługiwać SIGPIPE w skomplikowany sposób, tak jakbyA
działał w procesie potomnym i bez przerywania powłoki, aby zachowanie nie było zbyt zaskakujące przyB
wcześniejszym wyjściu. Znacznie łatwiej jest uruchomićB
proces nadrzędny.<<<
bash
nie wspiera<<<
, tak że operator pochodziły zzsh
jak$(<...)
przyszedł z ksh.W pierwszym przypadku po prostu używasz narzędzia grep i znajdujesz coś z pliku
/proc/meminfo
,/proc
jest to wirtualny system plików, więc/proc/meminfo
plik znajduje się w pamięci i pobranie jego zawartości zajmuje bardzo mało czasu.Ale w drugim przypadku tworzysz potok, a następnie przekazujesz wynik pierwszego polecenia do drugiego polecenia za pomocą tego potoku, co jest kosztowne.
Różnica wynika z
/proc
(ponieważ jest w pamięci) i potoku, zobacz poniższy przykład:źródło
W obu przypadkach wywołujesz zewnętrzne polecenie (grep). Wywołanie zewnętrzne wymaga podpowłoki. Rozwidlenie tej skorupy jest podstawową przyczyną opóźnienia. Oba przypadki są podobne, a zatem: podobne opóźnienie.
Jeśli chcesz odczytać plik zewnętrzny tylko raz i użyć go (ze zmiennej) wiele razy, nie wychodź z powłoki:
Co zajmuje tylko około 0,1 sekundy zamiast pełnego 1 sekundy dla połączenia grep.
źródło