Er du nysgjerrig på hvor lenge en prosess kjører og detaljer om ressursbruk? Linux-kommandoen `time` gir deg akkurat det – detaljert statistikk om tidsbruk og innsikt i ressursene programmene dine bruker.
`time` har flere varianter
Det finnes et bredt utvalg av Linux-distribusjoner og Unix-lignende operativsystemer. Hver av disse kommer med et standard kommandoskall. Bash-skallet er det vanligste i moderne Linux-distribusjoner, men alternativer som Z-skallet (zsh) og Korn-skallet (ksh) finnes også.
Alle disse skallene har sin egen `time`-kommando, enten som en innebygd kommando eller som et reservert ord. Når du skriver inn `time` i et terminalvindu, vil skallet vanligvis utføre sin interne kommando i stedet for å bruke GNU `time`-binærfilen som følger med Linux-distribusjonen.
For å dra nytte av flere alternativer og økt fleksibilitet, ønsker vi å benytte GNU-versjonen av `time`.
Hvilken `time`-kommando kjøres?
Du kan identifisere hvilken versjon av `time` som aktiveres ved hjelp av `type`-kommandoen. `type` informerer deg om skallet håndterer kommandoen internt, eller om den videresendes til GNU-binærfilen.
Skriv `type time` i terminalen og trykk Enter.
type time
I bash-skallet ser vi at `time` er et reservert ord. Det betyr at Bash som standard vil bruke sine interne `time`-rutiner.
type time
Også i Z-skallet (zsh) er `time` et reservert ord, og dermed vil de interne skallrutinene være standardvalget.
type time
I Korn-skallet er `time` et nøkkelord, og en intern rutine vil bli brukt i stedet for GNU `time`-kommandoen.
Hvordan kjøre GNU `time`-kommandoen
Hvis skallet i Linux-systemet ditt har en intern `time`-rutine, må du spesifisere at du ønsker å bruke GNU-`time`-binærfilen. Dette kan gjøres på flere måter:
- Angi hele filstien til binærfilen, for eksempel `/usr/bin/time`. Bruk kommandoen `which time` for å finne denne banen.
- Bruk kommandoen `command time`.
- Bruk en omvendt skråstrek før `time`-kommandoen: `\time`.
Kommandoen `which time` viser veien til binærfilen.
Vi kan teste dette ved å bruke `/usr/bin/time` som en kommando for å starte GNU-binærfilen. Det fungerer, og vi får et svar fra `time`-kommandoen som indikerer at vi ikke spesifiserte noen kommandolinjeparametere for den.
Å skrive `command time` gir samme resultat. Kommandoen `command` instruerer skallet om å ignorere den neste kommandoen, slik at den behandles utenfor skallet.
Å bruke en omvendt skråstrek `\` før kommandonavnet, har samme effekt som å bruke `command`.
Den enkleste måten å garantere at du bruker GNU `time`-binærfilen, er å bruke omvendt skråstrek: `\time`.
time
\time
`time` kaller skallversjonen av `time`, mens `\time` bruker `time`-binærfilen.
Hvordan bruke `time`-kommandoen
La oss analysere tidsbruken til noen programmer. Vi skal benytte to programmer, `loop1` og `loop2`, som er laget fra `loop1.c` og `loop2.c`. Disse programmene gjør ingen nyttig operasjon utover å demonstrere ineffektiv kode.
Dette er `loop1.c`. Lengden på en streng bestemmes en gang utenfor de to nestede løkkene.
#include "stdio.h" #include "string.h" #include "stdlib.h" int main (int argc, char* argv[]) { int i, j, len, count=0; char szString[]="how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek"; // get length of string once, outside of loops len = strlen( szString ); for (j=0; j<10000; j++) { for (i=0; i<len; i++) { count++; } } printf("count: %d\n", count); return 0; }
Og dette er `loop2.c`. Her beregnes lengden på strengen for hver iterasjon i den ytre løkken. Denne ineffektiviteten vil vise seg i tidsmålingene.
#include "stdio.h" #include "string.h" #include "stdlib.h" int main (int argc, char* argv[]) { int i, j, count=0; char szString[]="how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek"; for (j=0; j<10000; j++) { for (i=0; i<strlen( szString ); i++) { count++; } } printf("count: %d\n", count); return 0; }
La oss starte `loop1`-programmet og måle ytelsen med `time`.
\time ./loop1
La oss gjøre det samme for `loop2`.
\time ./loop2
Vi har nå to sett med resultater, men de er i et lite oversiktlig format. Vi kan jobbe med dette senere, men la oss se på noen nøkkelinformasjon.
Programmer opererer i to moduser når de kjører: brukermodus og kjernemodus. Prosessen veksler mellom disse modusene under kjøring.
I brukermodus har ikke en prosess direkte tilgang til maskinvare eller minne utenfor sin egen allokering. For å få tilgang til slike ressurser, må prosessen be kjernen om tillatelse. Hvis kjernen godkjenner forespørselen, går prosessen midlertidig inn i kjernemodus for å oppfylle forespørselen. Deretter går prosessen tilbake til brukermodus.
Resultatene for `loop1` viser at den brukte 0,09 sekunder i brukermodus. Den brukte enten null tid i kjernemodus, eller så er denne tiden for lav til å registreres. Den totale brukte tiden var 0,1 sekunder. `loop1` fikk i gjennomsnitt tildelt 89% av CPU-tiden i løpet av den totale brukte tiden.
Det ineffektive `loop2`-programmet tok tre ganger så lang tid å kjøre. Den totale brukte tiden er 0,3 sekunder. Brukermodus-behandlingstiden var 0,29 sekunder. Ingenting ble registrert for kjernemodus. `loop2` fikk i gjennomsnitt tildelt 96% av CPU-tiden under kjøringen.
Formatering av utdata
Du kan tilpasse utdata fra `time`-kommandoen med en formatstreng. Formatstrengen kan inneholde tekst og formatspesifikasjoner. En liste over formatspesifikasjoner finnes i man-siden for `time`. Hver spesifikasjon representerer et stykke informasjon.
Når strengen skrives ut, erstattes formatspesifikasjonene med deres faktiske verdier. For eksempel representeres prosentuell CPU-bruk med bokstaven `P`. For å indikere at en formatspesifikasjon ikke bare er en vanlig bokstav, legg til et prosenttegn: `%P`. La oss se et eksempel:
Alternativet `-f` (formatstreng) brukes for å indikere at det som følger er en formatstreng.
Vår formatstreng skal skrive ut teksten «Program:» etterfulgt av navnet på programmet (samt eventuelle kommandolinjeparametere). `%C`-formatspesifikasjonen står for «Navn og kommandolinjeargumenter for kommandoen som blir tidsbestemt». `\n` gir ny linje.
Det finnes mange formatspesifikasjoner, og de skiller mellom store og små bokstaver. Sørg for at du skriver dem riktig.
Deretter skal vi skrive ut teksten «Total tid: » etterfulgt av den totale brukte tiden for programkjøringen (representert av `%E`).
Vi bruker `\n` for ny linje. Så skriver vi ut «Brukermodus (s) » etterfulgt av CPU-tiden brukt i brukermodus, som er `%U`.
Vi bruker `\n` for ny linje. Denne gangen henter vi ut kjernetidsverdien. Vi skriver ut «Kjernemodus (s) » etterfulgt av formatspesifikasjonen for CPU-tid brukt i kjernemodus, som er `%S`.
Til slutt skal vi skrive ut «nCPU: » og den gjennomsnittlige prosenten av CPU-tid brukt av prosessen, som er formatspesifikasjonen `%P`.
Hele formatstrengen er pakket inn i anførselstegn. Vi kunne ha inkludert `\t`-tegn for å legge inn tabulatorer i utdataene om vi var nøye på justeringen av verdiene.
\time -f "Program: %C\nTotal time: %E\nUser Mode (s) %U\nKernel Mode (s) %S\nCPU: %P" ./loop1
Lagre utdata til en fil
For å holde oversikt over tidsresultatene fra testene dine, kan du lagre utdata fra `time` til en fil. Bruk alternativet `-o` (utdata). Programmets utdata vil fortsatt vises i terminalvinduet. Det er kun utdata fra `time`-kommandoen som omdirigeres til filen.
Vi kan kjøre testen på nytt og lagre utdata til filen `test_results.txt`:
\time -o test_results.txt -f "Program: %C\nTotal time: %E\nUser Mode (s) %U\nKernel Mode (s) %S\nCPU: %P" ./loop1
cat test_results.txt
Programutdataene fra `loop1` vises i terminalvinduet, mens resultatet fra `time` lagres i filen `test_results.txt`.
Hvis du vil lagre det neste settet med resultater i samme fil, må du bruke alternativet `-a` (legg til):
\time -o test_results.txt -a -f "Program: %C\nTotal time: %E\nUser Mode (s) %U\nKernel Mode (s) %S\nCPU: %P" ./loop2
cat test_results.txt
Det burde nå være klart hvorfor vi brukte formatspesifikasjonen `%C` for å inkludere programnavnet i formatstrengen.
Konklusjon
`time`-kommandoen er et kraftig verktøy, spesielt for programmerere og utviklere som finjusterer koden sin. Den er også nyttig for alle som ønsker å vite mer om hva som skjer under panseret når et program kjøres.