Koneessa käskyjen suoritusajat ovat epämääräisiä; joskus käsky kestää 2ms ja joskus se kestää 5ms. Ja alta puoli-millisekuntia kestävät suoritusajat pyöristetään nollaksi. Eikä aika ole tuonut tähän helpotusta eikä varmaan tuokaan - syyn tähän voisi esittää useammallakin tavalla:
- minkä intel tuo sen softa vie
- Torvalds esitti homman näin: "Linux ydin on turvonnut". Siis syyllinen on käyttöjärjestelmä. Saattaa käydä jopa niin että epämääräisyys alkaa kasvamaan sillä samalla kun ydin turpoaa niin sen ylläpitäjät vähenee eivätkä kerkiä tekemään aikaisemmin tehtyyn uusien lisäyksien vaatimia muutoksia.
Käskyn suoritusaikaa ei voi mitata mittaamalla kuinka kauan perättäisten samojen käskyjen suoritus keskimäärin kestää, sillä käskyä suoritettaessa sen koodi kännetään cacheen josta se toteutetaan.
- siis tällätavoin ei saa käskeä: for (( n=1; n<=1000; n++ )); do käsky; done
sillä kun seuraava käsky on sama kuin edellinenkin niin sen käännetty koodi on jo cachessa ja suoritus on tosinopea. Käskyn suoritusaika tuleekin mitata käskemällä esimerkiksi:
for (( n=1; n<=1000; n++ )); do { käsky; sleep .001}; done jolloin käsky-koodi joudutaa jokakerralla kääntämään uudestaan - loppuajassa täytyy sitten huomioda tuo että se "sleep .001":n aika täytyy vähentää.
Tuo mitattava on lauseessa: { echo a ; sleep .001 ;}
mitattava siinä on: echo a
ja kaikki muu on aina sama. Siis kun mitataan kauanko käsky:du kestää tulisi mitattavaksi: { du ; sleep .001 ;}
#!/bin/bash
# petteriIII 16.10.2015. muistutus: echo a kestää noin .17ms
# skripti on tarkoitettu käskyjen&"yksi tai monirivisten skriptien" suoritusajan luotettavaan mittaamiseen kun niiden suoritusajat vaihtelevat nolla-ajasta äärettömään aikaan. Mittausten
# toistettavuus pitkäkestoisissa mittauksissa on 0.1 ms, 10ms kestävissä mittauksissa 0.04ms ja alle puoli-millisekuntia kestävissä mittauksissa 0.01ms.
#
# skripti päättelee itse kuinka monesta mittauksesta sen tulee muodostaa keskiarvo. Mitattaessa alle millisekunnin kestäviä käskyjä mittausten lukumäärä on suuri ja mitattaessa yli sata
# sekuntia kestäviä käskyja mittausten lukumäärä on yksi.
#
# mitattavan käskyn on parasta antaa kirjoittaa näytölle. Mittaamisen aikana tietokoneen kanssa ei saa tehdä muuta.
function nolla () { : ;}
function mittaus () { alkuhetki=$(date +%s.%N) # echo '1 2 3 4' | awk '{print $2}' # echo '1 2 3 4' | cut -d ' ' -f 2 # tr -dc 3 < koe | wc -c
for (( n=1; n<=$mittauskerrat; n++ )); do { echo a ; sleep 5 ;} # ajoitettavan kaikki tulosteet menevät lattian alle kun lisää ajoitettavan perään: 2&>/dev/null
done ;}
# Pääohjelma
alkuhetki=$(date +%s.%N)
mittauskerrat=1; mittaus; aika=$(echo $(date +%s.%N)-$alkuhetki-.0000055 | bc )
mittauskerrat=$(echo 1000/$aika+1 | bc ); mittauskerrat=$(echo ${mittauskerrat#[-+]}); (( $mittauskerrat > 100000 )) && mittauskerrat=100000; lisamittausaika=$(echo $aika*$mittauskerrat | bc )
alkuhetki=$(date +%s.%N)
(( $mittauskerrat > 0 )) && echo 'Lisämittauksia tullaan suorittamaan noin:'$lisamittausaika' sekuntia'
for (( n=1; n<=$mittauskerrat; n++ )); do # n:än arvo määrää mittausten lukumäärän ja skripti itse määrää se arvon. 5.00208052080000000000
mittaus
done
echo $mittauskerrat':mittauksella saatu yhden mittauksen keskimääräinen aika sekunneissa= '$(echo $(date +%s.%N)/$mittauskerrat-$alkuhetki/$mittauskerrat-5.00174 | bc -l) | tee -a /tmp/lukemat
Tuloksien tarkistaminen käsin on teoriassa helppoa mutta siinä on kauhea työ. Esimerkiksi echo-käsky:
anna peräkkäin käskyt: time echo a; time echo aa;time echo aaa; ... ja pistä muistiin monenko a:n kohdilla user-aika muuttuu 0.000:sta 0.001:een ja edelleen 0.002:een.
- aika/merkki lasketaan: 0.001/((a-kirjainten luku kohdassa 0.002)-(a-kirjainten luku kohdassa 0.001))
- echo:n kutsumiseen kuluva aika (aika: echo a) on: ((a-kirjainten luku kohdassa 0.002)-(a-kirjainten luku kohdassa 0.001))/2*(yhden merkin tulosdtukseen kuluva aika eli edellinen tulos)
- likimääräisesti oikean paikan voi etsiä tämäntyyppisesti: merkkijono=$( printf "%$(echo 6300)s" | tr ' ' 'a'); time echo $merkkijono
- tai jos epäilee että tulostuva merkki vaikuttaa: merkkijono=$(cat /dev/urandom | base64 | head -c 6300); time echo $merkkijono
Siis tälläkertaa: muutokset tapahtuivat kohdilla 1915 ja 6300. Näistä laskettuna aika/merkki on 0.16mikrosekuntia ja echon kutsumiseen kuluva aika on .205millisekuntia - siis tuo minun skriptini ilmoittaa tuon pitemmän ajan elikä .205millisekuntia.
- ei tämä vielä ihan oikein ole mutta likimääräisesti se on. Kunnon mittauksen arvoihin voi luottaa paremmin.
**
Taas tuli uutta tietoa: tuo mittaus-funktion "sleep 0.001" on täysin riittämätön ja sen tilalle täytyy laittaa "sleep 5".
Tämä taas aiheuttaa sen että mittauksia tehdään kovin vähän ja harvakseen joten mittausaikaa täytyy myös säätää: uusittu mittaus kestää noin 17 minuuttia.
- mutta samassa yhteydessä selvisi, että tuo date antaa tosiaan tarkan ajan mikä käskyn suorituksessa kuluu joten sen antamia tuloksia voi keskiarvoistaa.
- eihän tuo keskiarvoistaminen muuta sitä tosiasiaa että sama käsky voi ääritapauksina toisinaan kestää millisekunnin ja toisellakertaa 10millisekuntia. Tämä taitaa johtua siitä että BASH on keskeytys-prioriteetiltaan kasan pohjimmainen.
- mutta nyt on mahdollista tutkia mitä olisi tehtävissä tuon pitkän odotusajan lyhentämiseksi ja se kertoisi kuinka skripteihin saa jämäkkyyttä.
- myös skriptin koodi on päivitetty.
**
cachet siis ovat yksi syy miksi käskyjen tarkka ajoittaminen vaatii pitkiä odotusaikoja. Linuksissa on käsky joka nollaa sellaiset cachet joissa ei ole mitään sellaista jota ei kovalevyltä löytäisi. Mutta sen laittaminen käskyjen ajoittamiseen ei ole hyvä idea sillä tuo käsky kestää milloin mitäkin, yleensä kuitenkin millisekunteja. Seuraava käsky käytääkin menetelmää jossa tuon cachejen tyhjennykseen menevä aika lasketaan jokakierroksella ja vähennetään siitä ajasta joka pitäisi odottaa. Käsky ja sen kutsu ovat seuraavat:
function torku () { alku=$(date +%S%N); sudo /sbin/sysctl vm.drop_caches; loppu=$(date +%S%N); sleep $(echo "($1-($loppu-$alku))/1000000000" | bc -l) ; }; sudo echo; time torku 100000000
jossa torkkuaika annetaan nanosekunneissa. Eihän torkku-aika ole edes yhtä tarkka kuin sleep-käskyssä mutta siitä ei ole kysymyskään sillä käskyllä haetaan vakautta tuolta drop_caches:ilta.
- huomenissa alan kokeilla tyhjentääkö tämä oikeat cachet.
- eipä tämä cachejen tyhjentäminen vaikuttanut.
- muuten taitaapa olla niin että käskyjen suoritusaika on aivan erilainen kun ne annetaan päätteeltä tai skriptistä; skriptistä ne toimivat paljon nopeammin.