Měření spotřebovaného času procesoru v Linuxu
16.3.2019
Ukážeme si jak v Linuxu zjistit spotřebovaný čas procesoru jak pro proces, tak pro jeho jednotlivá vlákna.
Měření časového intervalu s vysokou přesností
Pro měření časového intervalu v Linuxu použijeme funkci clock_gettime, která naplní 2. parametr (struktura timespec) časovým údajem s přesností až na nanosekundy. Reálnou přesnost lze v případě potřeby zjistit funkcí clock_getres. Časový údaj může být buď čas od začátku periody, nebo čas procesoru (CPU) který proces nebo thread spotřeboval. O jaký čas se bude jednat určíme v 1. parametru funkce příslušnou hodnotou, která je pro změření reálného času CLOCK_REALTIME, pro čas CPU procesu CLOCK_PROCESS_CPUTIME_ID a pro čas CPU vlákna CLOCK_THREAD_CPUTIME_ID. Nejprve si tedy vytvoříme pomocné třídy pro měření jednotlivých typů času:
// pro změření obecného časového intervalu
class Cas
{
protected:
timespec ts_start;
timespec ts_stop;
clockid_t typ = CLOCK_REALTIME;
public:
void start() noexcept
{
clock_gettime(this->typ, &this->ts_start);
}
void stop() noexcept
{
clock_gettime(this->typ, &this->ts_stop);
}
double interval() noexcept
{
return ts_stop.tv_sec - ts_start.tv_sec +
(ts_stop.tv_nsec - ts_start.tv_nsec) / 1000000000.;
}
};
// pro změření spotřebovaného procesorového času celého procesu
class CasCpuProcess : public Cas
{
public:
CasCpuProcess() noexcept
{
this->typ = CLOCK_PROCESS_CPUTIME_ID;
}
};
// pro změření spotřebovaného procesorového času jedntlivého vlákna
class CasCpuThread : public Cas
{
public:
CasCpuThread() noexcept
{
this->typ = CLOCK_THREAD_CPUTIME_ID;
}
};
Nyní si vytvoříme nějaký testovací kód ve kterém zaměstnáme procesor a který zavoláme v pararelních vláknech. Pro spuštění vláken využijeme standardní knihovnu C++ a její třídu std::thread. V prováděcí funkci vlákna budeme v cyklu volat funkci na zjištění, zda jejím vstup je prvočíslo a pro každé vlákno změříme procesorový čas vlákna a celkový čas provádění této funkce. Aby bylo odlišení těchto časů výraznější, přidáme volání funkce sleep, který nespotřebuje čas procesoru ale zvýší celkový naměřený čas.
bool je_prvocislo(unsigned int cislo) noexcept
{
unsigned int mez = (unsigned int)sqrt(cislo);
for (unsigned int i = 2; i <= mez; i++)
{
if (0 == cislo % i)
return false;
}
return true;
}
void funkce_thread(size_t pocet, int vlakno) noexcept
{
Cas cas;
CasCpuThread cas_thread;
cas.start();
cas_thread.start();
unsigned int pocet_prv = 0;
for (size_t i = 0; i < pocet; i++)
{
if (je_prvocislo(i))
pocet_prv++;
}
sleep(2); // 2 sekundy které nezaměstnají procesor
cas_thread.stop();
cas.stop();
printf("thread: %d, čas: %.6f, čas cpu thread: %.6f (počet: %d)\n",
vlakno, cas.interval(), cas_thread.interval(), (int)pocet_prv);
}
Nyní zbývá vytvořit 4 pararelně běžící vlákna a změřit celkový čas za který budou všechna ukončena a zjistit celkový spotřebovaný procesorový čas.
int main(int argc, const char** argv)
{
#define POCET_T 4
std::thread* threads[POCET_T];
Cas cas;
CasCpuProcess cas_process;
cas.start();
cas_process.start();
for (size_t i = 0; i < POCET_T; i++)
{
threads[i] = new std::thread(funkce_thread, 10000000, i);
}
for (size_t i = 0; i < POCET_T; i++)
{
threads[i]->join();
}
for (size_t i = 0; i < POCET_T; i++)
{
delete threads[i];
}
cas.stop();
cas_process.stop();
printf("čas celkem: %.6f, čas cpu procesu: %.6f\n",
cas.interval(), cas_process.interval());
printf("----------\nhotovo\n");
return EXIT_SUCCESS;
}
Jak je zřejmé z následujícího obrázku, při 4 vláknech na 4-jádrovém procesoru je celkový procesorový čas skoro 4-krát větší než absolutní čas potřebný na celý (pararelní) běh všech vláken (do kterého je započten čas pro funkci sleep(2)).
