Dorești să afli cât durează execuția unui proces și alte informații utile? Comanda `time` în Linux îți oferă statistici despre timp, dezvăluind detalii interesante despre resursele consumate de aplicațiile tale.
Comanda `time` și variantele sale
Există numeroase distribuții Linux și sisteme de operare de tip Unix, fiecare având un shell de comandă implicit. În majoritatea distribuțiilor Linux moderne, shell-ul implicit este bash, dar există și altele, precum zsh sau ksh.
Toate aceste shell-uri includ propria lor implementare a comenzii `time`, fie ca o comandă internă, fie ca un cuvânt rezervat. Când introduci `time` într-o fereastră de terminal, shell-ul va executa propria sa comandă, nu binarul GNU `time`, care vine odată cu distribuția Linux.
Noi vom prefera versiunea GNU a comenzii `time`, deoarece oferă mai multe opțiuni și este mai versatilă.
Află versiunea comenzii `time` folosită
Poți verifica ce versiune a comenzii `time` rulează folosind comanda `type`. Aceasta te va informa dacă shell-ul va gestiona instrucțiunea folosind propriile sale rutine interne sau dacă o va transmite binarului GNU.
Într-o fereastră de terminal, tastează `type time` și apasă Enter.
type time
În cazul shell-ului bash, `time` este un cuvânt rezervat, ceea ce înseamnă că bash va folosi propriile funcționalități interne.
type time

În shell-ul zsh, `time` este tot un cuvânt rezervat, deci se vor utiliza rutinele interne ale shell-ului.
type time

În shell-ul Korn, `time` este un cuvânt cheie și, similar, va fi folosită o rutină internă în locul comenzii GNU `time`.
Utilizarea comenzii GNU `time`
Dacă shell-ul tău Linux are o rutină internă pentru `time`, va trebui să specifici clar când vrei să folosești binarul GNU. Poți face acest lucru în una din următoarele modalități:
- Indicând calea completă către binar, de exemplu `/usr/bin/time`. Poți afla această cale cu comanda `which time`.
- Folosind comanda `command time`.
- Precedând comanda `time` cu un backslash: `\time`.

Comanda `which time` ne arată calea către binar.
Putem testa folosind `/usr/bin/time` pentru a lansa binarul GNU. Această metodă funcționează. Primim un mesaj de la comanda `time` care ne informează că nu am specificat niciun parametru pentru a funcționa.
Tastând `command time` obținem același rezultat. Comanda `command` îi spune shell-ului să ignore următoarea comandă, astfel încât să fie procesată în afara shell-ului.
Utilizarea unui backslash înaintea numelui comenzii are același efect ca utilizarea comenzii `command`.
Cea mai simplă metodă de a ne asigura că folosim binarul GNU `time` este să folosim backslash-ul.
\time
time

`time` invocă versiunea `time` a shell-ului, în timp ce `\time` utilizează binarul `time`.
Măsurarea timpului de execuție a programelor
Să măsurăm timpul de execuție al unor programe. Vom folosi două programe, `loop1` și `loop2`, create din `loop1.c` și `loop2.c`. Acestea nu fac ceva util în mod special, ci doar demonstrează efectele unui cod ineficient.
Iată codul din `loop1.c`. Lungimea unui șir este calculată o singură dată, în afara buclelor imbricate:
#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
Acesta este codul din `loop2.c`. Lungimea șirului este calculată de fiecare dată la fiecare iterație a buclei exterioare. Această ineficiență ar trebui să se observe în timpii de execuție:
#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
Să lansăm programul `loop1` și să-i măsurăm performanța cu `time`.
time ./loop1

Acum să facem același lucru și pentru `loop2`.
time ./loop2

Am obținut două seturi de rezultate, dar formatul este destul de neplăcut. Vom rezolva acest aspect mai târziu, dar haide să analizăm câteva informații din rezultate.
În timpul execuției unui program, acesta alternează între două moduri: modul utilizator și modul kernel.
Pe scurt, un proces în modul utilizator nu poate accesa direct hardware-ul sau memoria din afara alocării sale. Pentru a accesa astfel de resurse, procesul trebuie să trimită solicitări către kernel. Dacă kernelul aprobă cererea, procesul trece în modul kernel până când solicitarea este îndeplinită, apoi revine la execuția în modul utilizator.
Rezultatele pentru `loop1` ne indică faptul că `loop1` a petrecut 0,09 secunde în modul utilizator. Fie nu a petrecut timp în modul kernel, fie timpul petrecut în modul kernel este prea mic pentru a fi înregistrat. Timpul total scurs a fost de 0,1 secunde. `loop1` a avut o medie de 89% din timpul CPU alocat pe durata sa totală.
Programul ineficient `loop2` a avut nevoie de trei ori mai mult timp pentru a fi executat. Timpul total scurs a fost de 0,3 secunde. Timpul de procesare în modul utilizator a fost de 0,29 secunde. Nu se înregistrează nimic în modul kernel. `loop2` a avut o medie de 96% din timpul CPU alocat pe durata sa.
Formatarea rezultatului comenzii `time`
Poți personaliza rezultatul comenzii `time` folosind un șir de format. Acesta poate conține text și specificatori de format. Lista completă a specificatorilor de format poate fi găsită în pagina de manual pentru `time`. Fiecare specificator de format reprezintă o anumită informație.
Când este tipărit șirul, specificatorii de format sunt înlocuiți cu valorile reale pe care le reprezintă. De exemplu, specificatorul de format pentru procentul CPU este litera P. Pentru a indica comenzii `time` faptul că un specificator de format nu este o literă obișnuită, îi adăugăm un semn procent, cum ar fi `%P`. Să-l folosim într-un exemplu.
Opțiunea `-f` (șir de format) este folosită pentru a specifica comenzii `time` că ceea ce urmează este un șir de format.
Șirul nostru de format va afișa caracterele „Program: ” și numele programului (și orice parametri de linie de comandă dați programului). Specificatorul de format `%C` reprezintă „Numele și argumentele liniei de comandă ale comenzii care este cronometrată”. `\n` face ca ieșirea să treacă la următoarea linie.
Există mulți specificatori de format, iar aceștia sunt sensibili la majuscule și minuscule, așa că asigură-te că îi introduci corect.
În continuare, vom afișa caracterele „Timp total: ” urmate de valoarea timpului total scurs pentru această execuție a programului (reprezentată de `%E`).
Folosim `\n` pentru a introduce o altă linie nouă. Apoi, vom afișa caracterele „User Mode(s)”, urmate de valoarea timpului CPU petrecut în modul utilizator, reprezentată de `%U`.
Din nou, folosim `\n` pentru o linie nouă. De această dată ne pregătim pentru timpul nucleului. Vom afișa caracterele „Kernel Mode(s)”, urmate de specificatorul de format pentru timpul CPU petrecut în modul kernel, care este `%S`.
În final, vom afișa caracterele „nCPU: ” pentru a introduce o nouă linie și titlul acestei valori. Specificatorul de format `%P` ne va oferi procentul mediu al timpului CPU utilizat de procesul cronometrat.
Întregul șir de format este încadrat de ghilimele. Am fi putut include și caractere `\t` pentru a insera tab-uri, dacă doream alinierea valorilor.
time -f "Program: %C\nTotal time: %E\nUser Mode (s) %U\nKernel Mode (s) %S\nCPU: %P" ./loop1

Salvarea rezultatului într-un fișier
Pentru a păstra o evidență a timpilor obținuți din testele efectuate, poți redirecționa rezultatul comenzii `time` într-un fișier. Pentru a face acest lucru, folosește opțiunea `-o` (output). Rezultatul programului tău va fi în continuare afișat în fereastra terminalului, dar doar rezultatul comenzii `time` va fi redirecționat către fișier.
Putem relua testul și salva rezultatul în fișierul `test_results.txt`, astfel:
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

Ieșirea programului `loop1` este afișată în fereastra terminalului, iar rezultatele comenzii `time` sunt stocate în fișierul `test_results.txt`.
Dacă dorești să adaugi următorul set de rezultate în același fișier, trebuie să folosești opțiunea `-a` (append), astfel:
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

Acum ar trebui să fie clar de ce am folosit specificatorul de format `%C` pentru a include numele programului în rezultatul șirului de format.
Concluzie
Comanda `time`, extrem de utilă programatorilor și dezvoltatorilor pentru optimizarea codului, este benefică oricui dorește să afle mai multe despre ce se întâmplă în culise atunci când lansează un program.