Hur man använder tidskommandot på Linux

By rik

Vill du få reda på hur lång tid en process tar och mer därtill? Linux-kommandot `time` presenterar tidsstatistik, vilket ger intressanta detaljer om resurser som används av dina program.

`time` har många varianter

Det finns ett flertal Linux-distributioner och diverse Unix-liknande operativsystem. Alla dessa har ett förvalt kommandoskal. Det vanligaste standardskalet i moderna Linux-distributioner är bash. Dock finns det åtskilliga andra, som Z-skalet (zsh) och Korn-skalet (ksh).

Samtliga dessa skal har sitt eget `time`-kommando, antingen som ett inbyggt kommando eller som ett reserverat ord. När du skriver `time` i terminalen kommer skalet att använda sitt interna kommando istället för GNU-varianten som medföljer Linux-distributionen.

Vi vill använda GNU-versionen av `time` eftersom den har fler alternativ och är mer mångsidig.

Vilken version av `time` körs?

Du kan ta reda på vilken version som används genom att använda kommandot `type`. `type` informerar dig om skalet själv hanterar instruktionen med sina interna rutiner eller om det skickar den vidare till GNU-binären.

Öppna ett terminalfönster och skriv `type`, ett mellanslag och sedan ordet `time`, tryck på Enter.

type time

Vi ser att i bash-skalet är `time` ett reserverat ord. Det betyder att Bash använder sina egna interna tidsrutiner som standard.

type time

Även i Z-skalet (zsh) är `time` ett reserverat ord, så de interna skalrutinerna används som standard.

type time

I Korn-skalet är `time` ett nyckelord. En intern rutin används istället för GNU `time`-kommandot.

Använda GNU `time`-kommandot

Om skalet i ditt Linux-system har en intern `time`-rutin måste du ange specifikt att du vill använda GNU-binären. Du kan göra det på något av följande sätt:

  • Ange hela sökvägen till binären, exempelvis `/usr/bin/time`. Använd `which time` för att hitta sökvägen.
  • Använd kommandot `command time`.
  • Använd ett omvänt snedstreck före `time`.

Kommandot `which time` visar sökvägen till binären.

Vi kan testa detta genom att använda `/usr/bin/time` som kommando för att starta GNU-binären. Det fungerar. Vi får en respons från `time`-kommandot som talar om att vi inte angett några kommandoradsparametrar.

Att skriva `command time` fungerar också, och vi får samma användningsinformation. Kommandot `command` instruerar skalet att bortse från nästa kommando så att det behandlas utanför skalet.

Att använda ett omvänt snedstreck innan kommandot är samma sak som att använda kommandot `command` före kommandonamnet.

Enklaste sättet att säkerställa att du använder GNU-binären av `time` är att använda omvänt snedstreck.

time
\time

`time` startar skalets version av `time`. `\time` använder GNU-binären.

Använda `time`-kommandot

Låt oss tidmäta några program. Vi använder två program som heter `loop1` och `loop2`, vilka genererades från `loop1.c` och `loop2.c`. De gör inget användbart förutom att påvisa effekten av en typ av kodningseffektivitet.

Detta är `loop1.c`. Längden på en sträng beräknas inom de två kapslade looparna. Längden beräknas i förväg, utanför de kapslade looparna.

#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 ("%d\n", count);
 return 0;
}

Detta är `loop2.c`. Strängens längd beräknas om och om igen för varje cykel i den yttre loopen. Denna ineffektivitet bör synas i tidsmätningarna.

#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 ("%d\n", count);
 return 0;
}

Låt oss starta `loop1`-programmet och använda `time` för att mäta dess prestanda.

time ./loop1

Låt oss nu göra samma sak med `loop2`.

time ./loop2

Vi har nu fått två uppsättningar resultat, men de är inte särskilt lättlästa. Vi kan åtgärda det senare, men låt oss plocka ut lite information från resultaten.

När program körs finns det två lägen de växlar mellan. Dessa kallas användarläge och kärnläge.

Kortfattat kan en process i användarläge inte direkt komma åt hårdvara eller referera till minne utanför sin egen allokering. För att få tillgång till sådana resurser måste processen göra förfrågningar till kärnan. Om kärnan godkänner förfrågan går processen in i kärnlägeskörning tills kravet är uppfyllt. Processen växlar sedan tillbaka till användarlägeskörning.

Resultaten för `loop1` visar att `loop1` tillbringade 0,09 sekunder i användarläge. Den tillbringade antingen noll tid i kärnläge, eller så är tiden i kärnläge för kort för att registreras. Den totala förflutna tiden var 0,1 sekunder. `loop1` tilldelades i genomsnitt 89 % av CPU-tiden under den totala förflutna tiden.

Det ineffektiva programmet `loop2` tog tre gånger längre tid att köras. Dess totala förflutna tid är 0,3 sekunder. Bearbetningstiden i användarläge är 0,29 sekunder. Ingenting registreras för kärnläge. `loop2` tilldelades i genomsnitt 96 % av CPU-tiden under hela dess körning.

Formatera utdata

Du kan anpassa utdata från `time` med hjälp av en formatsträng. Formatsträngen kan innehålla text och formatspecifikationer. Listan över formatspecifikationer finns på man-sidan för `time`. Var och en av specifikationerna representerar en viss information.

När strängen skrivs ut ersätts formatspecifikationerna med de faktiska värden de representerar. Exempelvis är formatspecifikationen för procentandelen CPU bokstaven `P`. För att visa att en formatspecifikation inte bara är en bokstav, lägg till ett procenttecken, som `%P`. Låt oss använda det som ett exempel.

Alternativet `-f` (formatsträng) används för att signalera att det som följer är en formatsträng.

Vår formatsträng kommer att skriva ut tecknen ”Program:” och namnet på programmet (och alla kommandoradsparametrar). Formatspecifikationen `%C` står för ”Namn och kommandoradsargument för kommandot som tidsmäts”. `\n` gör att utskriften går vidare till nästa rad.

Det finns många formatspecifikationer och de är skiftlägeskänsliga, så se till att du anger dem korrekt.

Därefter kommer vi att skriva ut texten ”Total tid: ” följt av den totala förflutna tiden för programmets körning (representerad av `%E`).

Vi använder `\n` för att infoga en ny rad. Vi skriver ut texten ”Användarläge(s) ”, följt av värdet på CPU-tiden i användarläge, betecknat med `%U`.

Vi använder `\n` för att infoga ytterligare en ny rad. Den här gången förbereder vi oss för kärntidsvärdet. Vi skriver ut texten ”Kärnläge (s) ”, följt av formatspecifikationen för CPU-tid som spenderas i kärnläge, vilket är `%S`.

Slutligen skriver vi ut texten ”CPU: ”, för att ge en ny rad och rubriken för detta datavärde. Formatspecifikationen `%P` visar den genomsnittliga procentandelen av CPU-tid som används av processen.

Hela formatsträngen är innesluten i citattecken. Vi hade kunnat inkludera några `\t`-tecken för att lägga in tabbar i utskriften om vi var noggranna med justeringen av värdena.

time -f "Program: %C\nTotal tid: %E\nAnvändarläge (s) %U\nKärnläge (s) %S\nCPU: %P" ./loop1

Skicka utdata till en fil

För att registrera tiderna från de tester du utfört kan du skicka utdata till en fil. Använd alternativet `-o` (utdata) för att göra detta. Utskrift från ditt program kommer fortfarande att visas i terminalen. Det är bara utdata från `time` som omdirigeras till filen.

Vi kan köra testet igen och spara utdata i filen `test_results.txt` enligt följande:

time -o test_results.txt -f "Program: %C\nTotal tid: %E\nAnvändarläge (s) %U\nKärnläge (s) %S\nCPU: %P" ./loop1
cat test_results.txt

Programmets utdata `loop1` visas i terminalfönstret och resultaten från `time` hamnar i filen `test_results.txt`.

Om du vill lägga till nästa uppsättning resultat till samma fil måste du använda alternativet `-a` (lägg till) enligt följande:

time -o test_results.txt -a -f "Program: %C\nTotal tid: %E\nAnvändarläge (s) %U\nKärnläge (s) %S\nCPU: %P" ./loop2
cat test_results.txt

Nu borde det vara uppenbart varför vi använde formatspecifikationen `%C` för att inkludera programmets namn i formatsträngens utdata.

Tiden räcker inte

Även om kommandot `time` förmodligen är mest användbart för programmerare och utvecklare som finjusterar sin kod, är det också användbart för alla som vill lära sig lite mer om vad som händer bakom kulisserna varje gång ett program startas.