Skriptien profilointi ja tarpeettomien alikuorien välttäminen
Mittaa skriptin suoritusajat ja korvaa paljon uusia prosesseja luovat rakenteet, kuten cat- ja grep-ketjut, sisäänrakennetuilla vaihtoehdoilla.
Skriptien profilointi ja tarpeettomien alikuorien välttäminen on ilmainen Linux-komentorivin ja Bash-komentosarjojen hallinta-oppitunti CoddyKitissä. Tämä on oppitunti 1/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Linux-komentorivin ja Bash-komentosarjojen hallinta-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Linux-komentorivin ja Bash-komentosarjojen hallinta-kurssilla on yhteensä 4 oppituntia.
Miksi skriptin suorituskyvyllä on merkitystä
Hitaasti suorittuvat Bash-skriptit kuluttavat CI-aikaa, estävät cron-tehtäviä ja turhauttavat käyttäjiä. Suurin osa hitaudesta ei johdu monimutkaisesta logiikasta, vaan tarpeettomista prosessihaaroista: jokainen kutsumanne ulkoinen komento käynnistää uuden lapsiprosessin.
Tässä oppitunnissa opitte:
- mittaamaan, mihin aika todella kuluu
time- jabash -x-komennoilla - tunnistamaan paljon haaroja luovat haitalliset toimintamallit, kuten cat-komennon tarpeettoman käytön
- korvaamaan ulkoisia komentoja nopeammilla shellin sisäänrakennetuilla komennoilla
- käyttämään alishellejä tarkoituksellisesti ja välttämään niitä, kun ne eivät tuo lisäarvoa
Tavoitteena on kirjoittaa skriptejä, jotka tekevät saman työn käyttäen vähemmän lapsiprosesseja ja vähemmän seinäkelloaikaa.
Skriptin ajoittaminen time-sisäänrakennetulla komennolla
Yksinkertaisin profilointityökalu on shellin sisäänrakennettu time-komento. Liittäkää se minkä tahansa komennon tai putken eteen saadaksenne kolme mittaustulosta:
- real — kulunut seinäkelloaika (aika, jonka todella odotatte)
- user — käyttäjätilan koodiin käytetty suorittimen aika
- sys — ytimeen käytetty suorittimen aika (järjestelmäkutsut, I/O)
Suuri ero real-ajan ja user+sys-ajan välillä tarkoittaa yleensä, että skripti odottaa I/O-toimintoa tai käynnistää paljon lapsiprosesseja. Suorittakaa ensin time koko skriptin ympärillä varmistaaksenne ongelman olemassaolon ennen optimointia.
#!/usr/bin/env bash
# Time a whole script block
time {
for i in $(seq 1 1000); do
echo "line $i"
done | grep -c "5"
}
# Output example:
# 271
# real 0m0.045s
# user 0m0.038s
# sys 0m0.012sSuorituksen jäljitys bash -x:llä ja PS4:llä
bash -x tulostaa jokaisen komennon ennen sen suorittamista — tätä kutsutaan suorituksen jäljitykseksi. Sen avulla näette, mitkä rivit suoritetaan useimmin ja kutsutaanko ulkoisia ohjelmia odotettua enemmän.
Oletusarvoisesti jokaisen jäljitetyn rivin alussa on +. Voitte rikastaa etuliitettä PS4-muuttujalla lisäämällä siihen aikaleimoja, jolloin jäljityksestä tulee kevyt profilointityökalu:
PS4laajennetaan ennen jokaista jäljitettyä komentoa$EPOCHREALTIME-muuttujan (bash 5+) tai$(date +%s%N)-komennon lisääminen antaa nanosekuntitarkkuuden- Ohjatkaa vakiovirhe tiedostoon ja käsitelkää se jälkikäteen hitaiden osioiden löytämiseksi
#!/usr/bin/env bash
# Run with: bash -x ./myscript.sh 2>trace.log
# Or embed tracing inside the script:
export PS4='+ [${EPOCHREALTIME}] ${BASH_SOURCE}:${LINENO}: '
set -x
slow_function() {
local result
result=$(cat /etc/hostname) # fork — slow
echo "host: $result"
}
slow_function
set +x
# trace.log now contains timestamps so you can diff
# adjacent lines to find which step took longest.Mikä on tarpeeton alishell?
Alishell on nykyisen shell-prosessin lapsikopio. Se luodaan seuraavilla tavoilla:
- Komennon korvaus:
$(command) - Sulkeilla ryhmittely:
( commands ) - Putken ohjaaminen shell-rakenteeseen:
cmd | while read ...
Alishellit ovat tarpeellisia, kun todella tarvitsette eristystä tai putkea. Niistä tulee tarpeettomia, kun käytätte niitä vain sellaisen ulkoisen ohjelman kutsumiseen, jonka shell voisi käsitellä itse, tai kun kääritte sisäänrakennetun komennon ylimääräiseen haarautumiskerrokseen ilman syytä.
Jokainen alishellin haarautuminen maksaa nykyaikaisessa Linux-järjestelmässä noin 1–5 ms. Silmukassa, joka suoritetaan 10 000 kertaa, 1000 tarpeetonta alishelliä lisää 1–5 sekuntia pelkkää yleiskustannusta.
Klassinen haitallinen toimintamalli: cat-komennon tarpeeton käyttö
cat file | grep pattern on tunnetuin paljon haaroja luova haitallinen toimintamalli. Se käynnistää kaksi prosessia (cat + grep), jotka on yhdistetty putkella, vaikka grep voi lukea tiedoston suoraan.
Korjaus on yksinkertainen: välittäkää tiedostonimi suoraan komennolle, joka osaa käsitellä tiedostoja. Kun työkalu ei hyväksy tiedostonimiä, tätä kutsutaan syötteen uudelleenohjaukseksi; muutoin cat jätetään yksinkertaisesti pois.
- Hidas:
cat file | grep pattern— 2 prosessia, 1 putki - Nopea:
grep pattern file— 1 prosessi, ei putkea - Myös nopea:
grep pattern < file— 1 prosessi, stdin-uudelleenohjaus (ei putkipuskuria)
#!/usr/bin/env bash
# Create a sample file
seq 1 10000 > /tmp/numbers.txt
# --- Slow: useless cat ---
time cat /tmp/numbers.txt | grep -c "^5"
# --- Fast: grep reads the file directly ---
time grep -c "^5" /tmp/numbers.txt
# Both print the same count; the second is measurably faster
# because it skips the cat process and the inter-process pipe.Ulkoisten komentojen korvaaminen shellin sisäänrakennetuilla komennoilla
Monille yhden rivin muunnoksille on olemassa sisäänrakennettu vastine, joka välttää haarautumisen kokonaan. Verratkaa seuraavia yleisiä korvauksia:
echo ${#var}echo "$var" | wc -c-komennon sijaan — merkkijonon pituus${var^^}ja${var,,}echo "$var" | tr 'a-z' 'A-Z'-komennon sijaan — kirjainkoon muunnos (bash 4+)${var//search/replace}echo "$var" | sed 's/search/replace/'-komennon sijaan — yksinkertainen korvaus[[ "$var" =~ pattern ]]echo "$var" | grep -q pattern-komennon sijaan — säännöllisen lausekkeen täsmäytysread -r line < fileline=$(head -n1 file)-komennon sijaan — ensimmäisen rivin lukeminen
Mikään näistä sisäänrakennetuista komennoista ei haarauta lapsiprosessia. Säästö on kutsukohtaisesti pieni, mutta silmukoissa se kasvaa huomattavaksi.
#!/usr/bin/env bash
sentence="hello world from bash"
# --- Fork-heavy ---
upper_slow=$(echo "$sentence" | tr 'a-z' 'A-Z')
length_slow=$(echo "$sentence" | wc -c)
# --- Builtin equivalents (zero extra processes) ---
upper_fast=${sentence^^}
length_fast=${#sentence}
echo "Slow upper : $upper_slow"
echo "Fast upper : $upper_fast"
echo "Slow length: $length_slow"
echo "Fast length: $length_fast"Alishellien välttäminen silmukoissa
Komennon korvaaminen silmukan sisällä moninkertaistaa haarautumiskustannuksen iteraatioiden määrällä. Silmukka, joka suoritetaan 500 kertaa ja jossa on yksi $(date)-kutsu, käynnistää 500 lapsiprosessia pelkkiä aikaleimoja varten.
Strategioita silmukan yleiskustannuksen vähentämiseen:
- Siirtäkää invariantit komennot silmukan ulkopuolelle (laskekaa kerran ja käyttäkää tulosta uudelleen)
- Suosikaa aritmeettista laajennusta
$(( expr ))— se on sisäänrakennettu toiminto eikä haarautuminen - Käyttäkää
printf-komentoadate-komennon sijaan, kun tarvitaan vain muotoilua - Eräkäsitelkää ulkoiset kutsut: kerätkää tiedot ensin ja käsitelkää ne kerran silmukan ulkopuolella
#!/usr/bin/env bash
# Demonstrate: compute-once vs fork-per-iteration
# Bad: $(date) forks 1000 times
time (
for i in $(seq 1 1000); do
ts=$(date +%s) # fork each iteration
echo "$i $ts" > /dev/null
done
)
# Good: capture once, reuse
time (
ts=$(date +%s) # fork exactly once
for i in $(seq 1 1000); do
echo "$i $ts" > /dev/null
done
)Putken alishellit ja muuttujan näkyvyysalueen sudenkuoppa
Bashissa (toisin kuin ksh:ssa ja zsh:ssa) jokainen putken komento suoritetaan omassa alishellissään. Tämä tarkoittaa, että putken sisällä asetetut muuttujat menetetään, kun putki on suoritettu.
Tämä on sekä oikeellisuus- että suorituskykyongelma — saatatte ohjata syötteen while read-rakenteelle odottaen tietojen keräämistä, mutta huomata muuttujan olevan sen jälkeen tyhjä.
Kaksi ratkaisua:
- Käyttäkää prosessin korvausta
while read line; do ...; done < <(command)— while-silmukka suoritetaan nykyisessä shellissä, ei alishellissä - Käyttäkää lastpipe-valitsinta (
shopt -s lastpipe) — se suorittaa putken viimeisen osan nykyisessä shellissä (bash 4.2+)
#!/usr/bin/env bash
count=0
# --- Bug: count is always 0 after pipe (subshell) ---
seq 1 5 | while read -r n; do
(( count++ ))
done
echo "After pipe : count=$count" # prints 0
# --- Fix 1: process substitution (no subshell for while) ---
count=0
while read -r n; do
(( count++ ))
done < <(seq 1 5)
echo "Process sub : count=$count" # prints 5
# --- Fix 2: lastpipe option ---
shopt -s lastpipe
count=0
seq 1 5 | while read -r n; do
(( count++ ))
done
echo "lastpipe : count=$count" # prints 5Alishellin kustannusten mittaaminen mikrovertailulla
Alishellin yleiskustannus on helppo osoittaa pienellä vertailulla. Verratkaa aritmeettista operaatiota, joka tehdään $(( ))-laajennuksella (sisäänrakennettu toiminto), samaan operaatioon, joka ohjataan putkella expr-komennolle (ulkoinen prosessi).
Tyypillisessä Linux-järjestelmässä 10 000 expr-kutsua kestää noin 5 sekuntia, kun taas sama määrä $(( ))-kutsuja kestää alle 0,1 sekuntia — 50-kertainen ero identtiselle tulosteelle.
Tämä vertailumalli on hyödyllinen myös minkä tahansa optimoinnin mittaamiseen: suorittakaa molemmat versiot N kertaa silmukassa ja verratkaa tuloksia time-komennolla.
#!/usr/bin/env bash
N=500
# External command (fork per call)
time (
x=0
for ((i=0; i<N; i++)); do
x=$(expr $x + 1) # forks expr each time
done
echo "expr result: $x"
)
# Arithmetic builtin (no fork)
time (
x=0
for ((i=0; i<N; i++)); do
(( x++ )) # pure builtin
done
echo "builtin result: $x"
)Here-stringien käyttäminen echo-putkien välttämiseen
Yleinen toimintamalli on echo "$var" | command, jolla muuttuja välitetään standardisyötteeksi. Tämä haarauttaa kaksi prosessia (echo + command) ja luo putken. Here-string (<<<) tuottaa saman tuloksen vain yhdellä prosessilla — ulkoinen komento lukee ytimen hallitsemasta tilapäispuskurista.
grep pattern <<< "$var"— yksi prosessi, ei putkearead -r field1 field2 <<< "$line"— muuttujan jakaminen ilman ulkoista työkaluawc -w <<< "$sentence"— muuttujassa olevan tekstin sanamäärä
Here-stringit ovat erityisen hyödyllisiä tiukoissa silmukoissa, joissa jokaisella haarautumisella on merkitystä.
#!/usr/bin/env bash
data="The quick brown fox"
# --- Fork-heavy: echo spawns a child ---
word_count_slow=$(echo "$data" | wc -w)
echo "Slow word count: $word_count_slow"
# --- Fast: here-string, only wc spawns ---
word_count_fast=$(wc -w <<< "$data")
echo "Fast word count: $word_count_fast"
# --- Even better: use parameter expansion (zero forks) ---
# Split into array, count elements
read -ra words <<< "$data"
echo "Zero-fork count: ${#words[@]}"Käytännön uudelleenmuotoilu: ennen ja jälkeen
Käydään läpi realistinen lokitiedostoa käsittelevä skripti ja sovelletaan kaikkea opittua. Alkuperäinen versio ketjuttaa cat-, grep-, awk- ja tr-komennot putkilla. Uudelleenmuotoiltu versio vähentää prosessien määrän kahdeksasta kahteen.
Tehdyt keskeiset muutokset:
catpoistettiin —greplukee tiedoston suoraantr '[:lower:]' '[:upper:]'korvattiin muodolla${var^^}echo "$line" | grep -qkorvattiin muodolla[[ $line =~ ]]read -r-komentoa käytettiin prosessin korvauksen kanssa putkitetun while-silmukan sijaan
Suorittakaa uudelleenmuotoilun jälkeen time ./script.sh vahvistamisenne parannus. Mitatkaa aina — älkää olettako.
#!/usr/bin/env bash
# Create a sample log
printf 'ERROR: disk full\nINFO: started\nERROR: timeout\nINFO: done\n' \
> /tmp/sample.log
# === BEFORE (fork-heavy) ===
time (
cat /tmp/sample.log \
| grep 'ERROR' \
| while read -r line; do
label=$(echo "$line" | tr '[:lower:]' '[:upper:]')
echo "[ALERT] $label"
done
)
# === AFTER (builtin-first) ===
time (
while IFS= read -r line; do
echo "[ALERT] ${line^^}"
done < <(grep 'ERROR' /tmp/sample.log)
)Tietotesti: alikuoren näkyvyysalue
Testatkaa ymmärrystänne putkien alikuorista ja siitä, miten vältetään putken sisällä tehtyjen muuttujamuutosten menettäminen.
Oppitunnin kertaus: profiloikaa ensin, haarukoikaa vähemmän
Tässä oppitunnissa opitte tunnistamaan ja poistamaan yleisimmät tarpeettoman prosessien luonnin lähteet bash-komentosarjoissa.
Tärkeimmät opit:
- Käyttäkää
time-komentoa jaPS4-tiedoilla rikastettuabash -x-komentoa mittaamiseen ennen optimointia - Tarpeeton cat on yleisin huono ohjelmointikäytäntö — välittäkää tiedostonimet suoraan komennoille, jotka hyväksyvät ne
- Korvatkaa
echo "$var" | commandhere-stringillä (command <<< "$var") tai sisäänrakennetulla komennolla - Parametrilaajennukset (
${var^^},${var//s/r},${#var}) korvaavat monettr-,sed- jawc-komennot - Putkien alikuoret nielevät muuttujien muutokset — käyttäkää prosessien korvausta tai komentoa
shopt -s lastpipe - Siirtäkää muuttumattomat komentokutsut silmukoiden ulkopuolelle ja suosikaa
$(( ))-aritmetiikkaaexpr-komennon sijaan
Nyrkkisääntö: mitatkaa ensin, korvatkaa ulkoiset komennot sisäänrakennetuilla komennoilla aina kun mahdollista ja varmistakaa parannus toisella mittauksella.
Opi Bash tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 22
- Oppitunnit
- 88
Usein kysytyt kysymykset
Onko oppitunti ”Skriptien profilointi ja tarpeettomien alikuorien välttäminen” ilmainen?
Kyllä – oppitunnin ”Skriptien profilointi ja tarpeettomien alikuorien välttäminen” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Linux-komentorivin ja Bash-komentosarjojen hallinta-kurssin, päivitä CoddyKit PROhon. Linux-komentorivin ja Bash-komentosarjojen hallinta-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Skriptien profilointi ja tarpeettomien alikuorien välttäminen”?
Mittaa skriptin suoritusajat ja korvaa paljon uusia prosesseja luovat rakenteet, kuten cat- ja grep-ketjut, sisäänrakennetuilla vaihtoehdoilla. Harjoittelet Linux-komentorivin ja Bash-komentosarjojen hallinta-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni Linux-komentorivin ja Bash-komentosarjojen hallinta-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin Linux-komentorivin ja Bash-komentosarjojen hallinta-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 1/4.
Kuinka kauan ”Skriptien profilointi ja tarpeettomien alikuorien välttäminen”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä Linux-komentorivin ja Bash-komentosarjojen hallinta-oppitunnilla?
Kyllä. Jokainen Linux-komentorivin ja Bash-komentosarjojen hallinta-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- Skriptien profilointi ja tarpeettomien alikuorien välttäminen
- Rinnakkaisuus xargs -P:llä ja taustatöillä
- Työkuormien orkestrointi GNU parallelilla
- Suoratoistoputket ja nimetyt putket läpimenon parantamiseen