PDA

View Full Version : [C] Misurare tempo di esecuzione


Ed_Bunker
16-06-2004, 23:44
Dovrei realizzare una specie di mini server che accetti richieste di lettura ed operazioni varie sui file di una certa directory. Per ciascuna delle possibili operazioni previste il server deve mantenere traccia del tempo (medio) di esecuzione (Sia dato per scontato che cio' sia fatto...). In base a tale statistica verificare se una certa richiesta (Riconosciuta e di cui guardo il tempo medio di exec...) richieda un tempo di exec maggiore di una fork(): se si l'operazione viene svolta dal proc figlio altrimenti dal proc padre.
Volendo fare tale controllo sul tempo di exec, dovrei effettuare ogni volta una fork() misurandone il tempo ovvero:

....
struct timeval start, end;
gettimeofday(&start, NULL);
fork();
gettimeofday(&end, NULL);
long execTime = (end.tv_sec - start.tv_sec) + (end.tv_usec - end.tv_usec);
....

Ma, a questo punto, una fork() e' gia' stata eseguita ed un nuovo processo e' in exec . No ?!? E quindi, anche se la richiesta avesse un tempo di exec. inferiore ad una fork() che vantaggio ne trarrei ?!?! Sono assai confusetto...

thks

ilsensine
17-06-2004, 08:08
Sì anch'io. Puoi spiegare meglio cosa devi fare? Magari con qualche esempio...

Ed_Bunker
17-06-2004, 10:20
Originariamente inviato da ilsensine
Sì anch'io.
:confused: :confused:
Originariamente inviato da ilsensine
Puoi spiegare meglio cosa devi fare? Magari con qualche esempio...
Allora, brevemente... Un processo server deve mettere a disp. di un imprecisato numero di client delle operazioni su file di testo. Operazioni del tipo: ricerca di una certa sottostringa all'interno di un file dato, restituzione di una parte di testo di un certo file dati offset e lunghezza del testo cercato, etc...
Il server accetta richieste dai client attraverso una pipe con nome nota e creata all'avvio dal server. Le risposte del server ai client avvengono sulle pipe che i client stessi specificano, inviandone il nome (Attraverso la pipe del server) al server stesso.

Vedi ESEMPIO (http://xoomer.virgilio.it/ste.iarda/Example1.jpg) e relativa LEGENDA (http://xoomer.virgilio.it/ste.iarda/Legenda.jpg).

Il server, alla ricezione di una generica richiesta:

-controlla che la richiesta sia "lecita" (Il modo i cui lo faccia per ora non ci interessa).
-se il controllo precedente e' soddisfatto controlla (Accedendo ad una tabella privata, magari...) qual e' il tempo medio di exec. per soddisfare tale richiesta. Confronta tale valore (TEMPO_FUN) con quello rischiesto per fare una fork() (TEMPO_FORK) (Il tempo per fare una fork() viene calcolato come sopra...) e quindi:

TEMPO_FUN > TEMPO_FORK ==> il server crea un processo figlio il quale soddisfera' la richiesta per poi terminare. Il server avra' cosi' la possibilita' di restare "in acolto" di nuove richieste.
TEMPO_FORK > TEMPO_FUN ==> il server richiama direttamente la funzione in grado di soddisfare la richiesta del client senza dare il controllo a nessun processo figlio.

Ad ogni modo si terra' traccia del tempo impiegato a soddisfare la richiesta (Dal padre o dal figlio) in modo da poterne aggiornare (Nella tabella privata) il valore corrispondente del tempo medio di exec.

Adesso e' piu' chiaro ??
La mia perplessita' e' la stessa...

ilsensine
17-06-2004, 10:46
Originariamente inviato da Ed_Bunker
:confused: :confused:

Anch'io ero assai "confusetto" dalla tua descrizione :D


Vedi esempio (http://xoomer.virgilio.it/ste.iarda/images/Example1.jpg) e relativa legenda (http://xoomer.virgilio.it/ste.iarda/images/Legenda.jpg).

chmod +x images
chmod +r images/*
;)


TEMPO_FUN > TEMPO_FORK ==> il server crea un processo figlio il quale soddisfera' la richiesta per poi terminare. Il server avra' cosi' la possibilita' di restare "in acolto" di nuove richieste.
TEMPO_FORK > TEMPO_FUN ==> il server richiama direttamente la funzione in grado di soddisfare la richiesta del client senza dare il controllo a nessun processo figlio.

Ovviamente la exec è un pò più lenta della fork. Il tuo problema però mi sembra più idoneo ad essere approcciato con i thread.

fpucci
17-06-2004, 10:50
TEMPO_FORK lo calcoli una volta per sempre all'inizio dei tempi, quando tiri su il server e prima che esso dia la disponibilità ai client di collegarsi ad esso. E questo è il tuo tempo di riferimento (ci devi aggiungere anche il tempo di esecuzione di una exec() e di una eventuale wait() ).

A questo punto, ad ogni richiesta, non c'è bisogno di invocare la fork() se TEMPO_FORK > TEMPO_FUN.
:)

ilsensine
17-06-2004, 10:50
Originariamente inviato da ilsensine
Ovviamente la exec è un pò più lenta della fork. Il tuo problema però mi sembra più idoneo ad essere approcciato con i thread.
Aspetta c'è qualcosa che non mi quadra...una exec deve essere comunque preceduta da una fork, altrimenti il processo chimante termina...

Ed_Bunker
17-06-2004, 11:19
Originariamente inviato da fpucci
TEMPO_FORK lo calcoli una volta per sempre all'inizio dei tempi, quando tiri su il server e prima che esso dia la disponibilità ai client di collegarsi ad esso. E questo è il tuo tempo di riferimento (ci devi aggiungere anche il tempo di esecuzione di una exec() e di una eventuale wait() ).

A questo punto, ad ogni richiesta, non c'è bisogno di invocare la fork() se TEMPO_FORK > TEMPO_FUN.
:)
Non mi occorre fare nessuna exec() poiche' dopo la fork avro' processo padre e processo figlio. (Eventualmente...) Il primo si rimette in attesa di ulteriori richieste, il secondo esegue la funzione richiesta. ma tale funzione si trova all'interno del codice del padre e quindi non e' necessaria nessuna exec.

/*Tralasciando, per ora, il discorso sul controllo del controllo sui tempi di exec e supponendo di fare eseguire sempre la richiesta mediante un processo figlio...*/

...
...

int main(....)
{
...
int pid;
if ((pid = fork()) == -1)
{
perror("Errore nella fork();
exit(0);
}
if(pid)/*Sono il padre*/
{
/*Mi metto in ascolto di ulteriori richieste*/
}
if (pid == 0)/*Sono il figlio*/
{
/*Per esempio...*/
funzione1();
exit(0);
}
...
...
}

void funzione1()
{
...
}

void funzione2()
{
...
...
}

void funzione3()
{
...
...
}


Pertanto nessun problema per la exec. Ma...

Ed_Bunker
17-06-2004, 11:21
Originariamente inviato da ilsensine
Aspetta c'è qualcosa che non mi quadra...una exec deve essere comunque preceduta da una fork, altrimenti il processo chimante termina...

Non e' che il processo chiamante termina, continua la sua esecuzione ma eseguendo un altro codice...

ilsensine
17-06-2004, 11:43
Originariamente inviato da Ed_Bunker
Non e' che il processo chiamante termina, continua la sua esecuzione ma eseguendo un altro codice...
Sì ma all'atto pratico è lo stesso. Se il processo server chiama exec, ci sarà un altro processo in esecuzione. Il processo server termina lì, e non può più essere recuperato se non rilanciandolo ex novo.

Ed_Bunker
17-06-2004, 11:57
Originariamente inviato da ilsensine
Sì ma all'atto pratico è lo stesso. Se il processo server chiama exec, ci sarà un altro processo in esecuzione. Il processo server termina lì, e non può più essere recuperato se non rilanciandolo ex novo.
In "soldoni' si... La exec non ritorna mai (A meno di errori) ma il processo e' lo stesso (Stesso PID...), ovviamente non sara' piu' un server, poiche' il codice che andra' ad eseguire sara' un altro...

ilsensine
17-06-2004, 12:08
E' un pò strano come design, il server dovrebbe continuare ad essere attivo anche dopo aver soddisfatto la richiesta...

Comunque sia, la fork dovrebbe essere più veloce.

Ed_Bunker
17-06-2004, 12:13
Originariamente inviato da ilsensine
E' un pò strano come design, il server dovrebbe continuare ad essere attivo anche dopo aver soddisfatto la richiesta...

Comunque sia, la fork dovrebbe essere più veloce.

Allora forse mi sono spiegato proprio male.... E' ovvio che il server rimanga attivo. Altrimenti che roba sarebbe ?!?!? Dato che NESSUNA exec() viene fatta (Ne' dal processo figlio ne', tantomeno, dal padre) il server resta sempe attivo.

ilsensine
17-06-2004, 12:19
Ah te intendevi "exec" per "esecuzione"....credevo stessi parlando della _funzione_ exec, spesso usata insieme a fork :D

Ed_Bunker
17-06-2004, 12:45
Originariamente inviato da ilsensine
Ah te intendevi "exec" per "esecuzione"....credevo stessi parlando della _funzione_ exec, spesso usata insieme a fork :D

Ecco dove nasceva l' "incomprensione" ! :muro: :D :D
Era tanto per abbreviare un po'... ;)

ilsensine
17-06-2004, 12:47
Ora che l'arcano è svelato, cosa vuoi sapere? Come ottenere la latenza di una fork?

Ed_Bunker
17-06-2004, 13:43
Quello che vorrei sapere e' semplicemente quello che ho detto fin dall'inizio: :D

Originariamente inviato da Ed_Bunker
.....
Ma, a questo punto, una fork() e' gia' stata eseguita ed un nuovo processo e' in exec . No ?!? E quindi, anche se la richiesta avesse un tempo di exec. inferiore ad una fork() che vantaggio ne trarrei ?!?! Sono assai confusetto...

thks

ilsensine
17-06-2004, 13:55
Una fork è implementata in maniera molto veloce, però ci sono un paio di cose che devi considerare:

- Normalmente dopo la syscall fork, il controllo passa al processo figlio. Non puoi però fare assunzioni su questo, in quanto potrebbe benissimo variare da kernel a kernel.

- La fork è veloce perché è efficientemente implementata tramite la tecnica COW; la semplice latenza della chiamata fork però non è il solo overhead che devi considerare, ma anche la tecnica COW che viene utilizzata. Ti spiego in due parole di cosa si tratta:
COW -- copy-on-write
Per rendere meno pesante per il sistema il fork di un processo, le pagine di memoria inizialmente _non_ sono duplicate: ne viene conservata una sola copia, però nello stato di sola lettura. Quando uno dei due processi tenta di scrivere su una pagina di memoria, essendo questa in sola lettura il kernel genera una eccezione di accesso, e solo allora duplica fisicamente la pagina interessata (e solo quella pagina).
La tecnica COW consente quindi di effettuare fork molto veloci, e di risparmiare molto sulla memoria utilizzata (le pagine che vengono solo lette, come librerie ecc., non verranno mai duplicate!), al prezzo però di qualche minor page fault in più.

Ciò detto, non so se nel tuo caso la fork introduca un overhead accettabile o meno: a meno che non stai facendo una applicazione veramente time-critical, non noterai il suo impatto. Sicuramente se fai dell'I/O su file, il costo della fork è assolutamente trascurabile.

Ed_Bunker
17-06-2004, 14:37
Originariamente inviato da ilsensine
Una fork è implementata in maniera molto veloce, però ci sono un paio di cose che devi considerare:

- Normalmente dopo la syscall fork, il controllo passa al processo figlio. Non puoi però fare assunzioni su questo, in quanto potrebbe benissimo variare da kernel a kernel.

- La fork è veloce perché è efficientemente implementata tramite la tecnica COW; la semplice latenza della chiamata fork però non è il solo overhead che devi considerare, ma anche la tecnica COW che viene utilizzata. Ti spiego in due parole di cosa si tratta:
COW -- copy-on-write
Per rendere meno pesante per il sistema il fork di un processo, le pagine di memoria inizialmente _non_ sono duplicate: ne viene conservata una sola copia, però nello stato di sola lettura. Quando uno dei due processi tenta di scrivere su una pagina di memoria, essendo questa in sola lettura il kernel genera una eccezione di accesso, e solo allora duplica fisicamente la pagina interessata (e solo quella pagina).
La tecnica COW consente quindi di effettuare fork molto veloci, e di risparmiare molto sulla memoria utilizzata (le pagine che vengono solo lette, come librerie ecc., non verranno mai duplicate!), al prezzo però di qualche minor page fault in più.

Ciò detto, non so se nel tuo caso la fork introduca un overhead accettabile o meno: a meno che non stai facendo una applicazione veramente time-critical, non noterai il suo impatto. Sicuramente se fai dell'I/O su file, il costo della fork è assolutamente trascurabile.

L'efficienza non e' un problema basilare nel caso di una situazione come la mia. Della tecnica COW avevo gia' letto. Era nell'ambiro di S.O.... non ricordo bene... Mi sembra che figlio e padre condividano dati fintanto che uno dei due non faccia un tentativo di aggiornamento/scrittura su essi. No ?!? Comunque, a parte questo, il fatto e':

ad ogni richiesta "devo" misurare il tempo di esecuzione della fork() ed in base a cio' decidere ?!? In tal caso la mia domanda e':
cosa ci guadagno in termini di efficienza visto che oramai una fork() e' stata eseguita e, quindi, l'eventualita' che l'operazione richiesta possa essere svolta in meno tempo di quello necessario per la fork() e' svanita ?!?

ilsensine
17-06-2004, 14:42
Originariamente inviato da Ed_Bunker
ad ogni richiesta "devo" misurare il tempo di esecuzione della fork() ed in base a cio' decidere ?!? In tal caso la mia domanda e':
cosa ci guadagno in termini di efficienza visto che oramai una fork() e' stata eseguita e, quindi, l'eventualita' che l'operazione richiesta possa essere svolta in meno tempo di quello necessario per la fork() e' svanita ?!?
Non mi sembra molto fattibile. Primo, perché se la fork è stata eseguita, ormai il più è fatto. Secondo, perché dovresti trovare un modo per dire al processo figlio (che hai ormai lanciato) "fermati, annulla tutto, ci penso io". Ammesso che sei ancora in tempo a farlo, ovviamente.

C'è un qualche motivo per cui non puoi usare i thread?

Ed_Bunker
17-06-2004, 15:50
Originariamente inviato da ilsensine
Non mi sembra molto fattibile. Primo, perché se la fork è stata eseguita, ormai il più è fatto. Secondo, perché dovresti trovare un modo per dire al processo figlio (che hai ormai lanciato) "fermati, annulla tutto, ci penso io". Ammesso che sei ancora in tempo a farlo, ovviamente.

C'è un qualche motivo per cui non puoi usare i thread?
Il fatto e' che il tutto riguarda un progetto per l'universita' ed i thread non sono stati materia del programma. Anche se probabilmente sarebbero la sol. ideale...

Ed_Bunker
18-06-2004, 22:17
Qualche altro suggerimento (Magari piu' "elegante"...) ?!?

:D :D :)