Newsgroup: it.comp.lang.c
Titolo: Pthreads e flow control

Pensavo sempre per lo stesso programma del razz di cui parlo
abbondantemente, di provare anche i threads per aumentare le
performances.
In python uso multiprocessing e funziona molto bene, il concetto sarà
più o meno lo stesso, ho bisogno di
- eseguire N / K simulazioni per ognuno dei K thread
- unire i risultati in un unico array
Si riesce a sapere a runtime quale potrebbe essere il numero di task
ottimali da lanciare?
Sono indeciso tra l'altro su come dividere i vari casi (con thread,
senza threads, con trucchi senza trucchi), potrei:
- usare delle macro e usare #ifdef per poter poi compilare il codice
  corretto diversamente (e volendo mettere nel makefile diversi
  eseguibili da generare, come già faccio per il debug e il profiling)
- passare degli argomenti e interpretarli con getopt per poi eseguire il
  codice corrispondente. Magari usando qualche bel puntatore a funzione
  per decidere.
Altra domanda, come mai se scrivo una funzione, non ne dichiaro il
prototipo da qualche parte ma non la uso da nessuna parte il compilatore
non mi dà nessuno warning?
Forse perché la cancella del tutto notando che non serve a nulla?
(un unused function come fa per le variabili potrebbe comunque darlo
no?)
Grazie a tutti

Visto che il tuo codice, a quanto ho capito, esegue delle statistiche 
tramite un generatore di numeri casuali, puoi ottenere lo stesso 
risultato semplicemente lanciando più volte lo stesso codice e facendo 
la media dei risultati. Il numero di istanze ottimale dovrebbe essere 
pari al numero di CPU del tuo computer. Unica attenzione è quella di 
inizializzare il generatore dei numeri casuali con semi diversi.
E.
Andrea Crotti ha scritto:


 E.
Non so se fare la media è una cosa tanto astuta, voglio evitare
divisioni il più possibile (per approssimazioni floating point).
Tra l'altro in effetti adesso il seed non lo inizializzo, vuol dire che
ottengo sempre la stessa sequenza o comunque ha una qualche
inizializzazione sua?
Ottenere il numero di cpu con pthread è possibile?

 Ottenere il numero di cpu con pthread è possibile?
Onestamente non lo so e comunque per un problema come quello in oggetto
non andrei mai a perdere il tempo piu' prezioso (il mio...)
per implementare una cosa con threads, che in questo
caso e' assolutamente sprecata.
Come ti hanno gia' detto la cosa piu' semplice e' far partire
un numero appropriato di programmi e poi tirare le somme.
Tu qui hai un parallelismo a grana grossissima e nessuna
interazione tra i thread.
Non avrai 10,000 CPU o core. Mettiamo che tu ne abbia 8.
Fai partire 8 copie del programma, ognuno col suo seme
casuale (tipo: 1, 2, 3, 4, 5, 6, 7, 8).
Alla fine fai le medie che consistono nel sommare 8
valori e dividere per 8.
Vedi bene che fare 1 divisione non ha assolutamente
nessun impatto sulle performance.
Fare alcune decine di miliardi di divisioni e' un conto,
farne una decina e' insignificante.
Anche per quel che riguarda la precisione, tutto sommato
la divisione (per un numero intero) e' una delle operazioni
piu' innocue.
(Ti suggerisco la lettura di questo documento:
http://dlc.sun.com/pdf/800-7895/800-7895.pdf
 )
Se avrai l'accortezza di usare numeri double al posto dei float,
vedrai che la precisione dei calcoli che puoi ottenere e' comunque
di svariati ordini di grandezza superiore alla significativita'
dei dati stessi, ottenuti per via statistica.
Quello che voglio dire e' che se tu di un esperimento
(non sto parlando del caso in oggetto che non conosco)
fai 1000 prove e ottieni come media 7552/1000 = 7.552 gia' rappresentare
il dato con 3 cifre decimali e' una bella dimostrazione
di ottimismo, perche' a meno che la varianza dei dati non sia
bassissima, la probabilita' che le ultime 2 cifre (52)
decimali siano esatte e' molto bassa.
ciao,
g.

 Ottenere il numero di cpu con pthread è possibile?
Non conosco pthread...
E.

 seme casuale, oppure lo genero a caso e poi lo salvo.
Mm sì mi sembra giusto, farò un po' di esperimenti con i semi e vedo se
cambia qualcosa.

Grazie dell'ottima spiegazione, voglio usare pthread anche perché
- voglio tenere puro C e non lanciare processi da shell
- voglio imparare a scrivere threads in C ;)
Il documento lo conoscevo ed è veramente ottimo, in effetti 3 cifre
decimali mi bastano già, quindi stavo chiaramente esagerando, ma se
posso tenere dei long non vedo perché andare a tirar fuori le
percentuali.

 performances.
Nota che l'accostamento "pi� threads = maggiori performance" � un
fraintendimento molto comune. Non � sempre cos�. Anzi, a volte il MT
affatica e basta. Un'architettura multithread pu� essere efficace quando
ci sono dei tempi morti da sfruttare (tipicamente attese I/O o richieste
di qualche tipo sincrone) e quindi il parallelismo � efficace perch�,
mentre qualcuno aspetta, qualcun altro pu� sfruttare la CPU; ma decidere
di buttarsi sul MT a prescindere pu� essere disastroso. A parte che
un'architettura di questo tipo � molto pi� difficile da programmare e
debuggare, � possibile che avere pi� processi sia la scelta migliore. Un
processo svolge un task e si programma abbastanza facilmente. Si
parametrizza il suo lavoro trasmettendogli (mediante IPC) i dati che deve
elaborare e si aspetta che completi. Volendo, si pu� scalare il sistema
aggiungendo (staticamente o dinamicamente) pi� processi. Io in molti casi
l'ho trovata la soluzione pi� comoda. 
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 l'ho trovata la soluzione più comoda. 
Sì chiaro, nel mio caso non ho I/O e ho già visto che con python e
multiprocessing raddoppio quasi le performances, per cui ho dedotto che
dovrei avere più o meno un risultato analogo.
A patto naturalmente che i phtread vadano d'accordo con il sistema e
vengano eseguiti sui due processori in parallelo, ma penso questo sia
scontato, o no?

 l'ho trovata la soluzione pi� comoda.
Se e' necessario usare piu' processi, non c'e' differenza tra
thread e processi, dal punto di vista del parallelismo.
Solo che i processi sono molto piu' pesanti e IPC interprocesso
occupa mediamente piu' risorse di meccanismi di comunicazione tra
thread nello stesso processo.
In generale, se serve parallelizzale le operazioni, i thread sono
piu' efficenti di un analoga architettura che usi solo
processi come fossero thread :-)
bb.

 thread e processi, dal punto di vista del parallelismo.
La pesantezza dei processi rispetto ai thread dipende dal sistema
operativo (sotto Windows, per es., sono mediamente più pesanti che
sotto Unix/Linux). L'efficienza maggiore o minore è vincolata ai
compiti da svolgere. E' probabile che un'applicazione multithread
possa essere realizzata come multiprocesso senza perdere in
performance. La programmazione multithread è complessa e, come ho già
scritto, difficileda debuggare. Oltretutto non c'è lo stesso
isolamento come tra i processi, per cui è possibile che, crashando un
thread, tutta l'applicazione vada a picco. Un'applicazione
multiprocesso, poi, può scalare senza richiedere interventi sul
codice.

 Un'applicazione multiprocesso, poi, pu� scalare senza richiedere
 > interventi sul codice.
Questa e' da spiegare...
Se vuoi dire che puoi lanciare 1 o N processi, non cambia molto,
l' applicazione a thread puo' essere scritta per dimensionare a
run-time i thread che usa, con un piccolo sforzo di progetto.
E comunque ogni processo consuma moolte piu' risorse macchina
dell' equivalente a thread, per cui la scalatura dipende
dalle risorse disponibili.
bb.

Se vuoi dire che puoi lanciare 1 o N processi, non cambia molto,
l' applicazione a thread puo' essere scritta per dimensionare a
run-time i thread che usa, con un piccolo sforzo di progetto.
Dimensionare a run-time i thread significa dover gestire una
configurazione del processo: più codice da scrivere, debuggare,
mantenere. Un processo, invece, se parametrizzato a dovere, può essere
inserito in un pool di processi in maniera assolutamente indolore.
Io ho scritto un'applicazione multiprocesso che si interfaccia a un
sistema remoto e funziona egregiamente. Non mi sono preoccupato di
dover gestire tutto l'apparato MT, rendendo il codice del processo
molto più snello e semplice. Se mi serve aumentare o diminuire il
numero di processi, mi basta modificare il batch che lancia il pool di
processi. Il tutto, come già detto, funziona bene.
Comunque non starò qui a polemizzare sul MP vs. MT. Sei libero di
usare la strada che vuoi. C'è molta letteratura in giro a proposito
dei vantaggi del MP sul MT e viceversa. In rete trovi molte risorse.

Ho aperto un branch di test per la soluzione a thread, dopo aver trovato
quest'ottimo tutorial
https://computing.llnl.gov/tutorials/pthreads/
Ci sono alcune cose che non mi tornano.
In sostanza se ho capito bene
- creo i vari thread
- gli passo UN solo argomento (ho creato una struttura con dentro tutto
  il necessario)
- faccio ritornare qualcosa a questa funzione che chiamo che dev'essere
  un puntatore a void.
- faccio il join del tutto e unisco i risultati
Ora ho qualche dubbio sui vari casting da fare e in generale mi sembra
che venga un po' una schifezza, il ragionamento è corretto comunque?
Dall'estero del "loop" creo le varie strutture in modo che le cose
immutabili siano puntatori alle variabili globali mentre quelle mutabili
siano copie...
--8<---------------cut here---------------start------------->8---
void *
loop(Config conf) {
     int i, rank;
     long *result;
     for (i = 0; i < conf.num_simulations; i++) {
          conf.deck->len = conf.deck->orig_len;
          // restore the hand to the initial state at every loop
          conf.hand_tmp = *hand_init;
          play(&conf.deck, num_players, &conf.hand_tmp);
          rank = rank_hand(&conf.hand_tmp);
          assert(rank); // different from 0
          result[rank_to_result_idx(rank)]++;
     }
     return (void *) result;
}
--8<---------------cut here---------------end--------------->8---


Se loop e' il tuo thread, dovrebbe essere:
void* loop(Config* config)
cambiando di conseguenza config. con config->
bb

 ?????
Volevo semplicemente dire che il mio loop prendeva dei puntatori e
andava a modificare il loro contenuto, se devo renderlo parallelo devo
fare in modo che questo non succeda.
Quindi i valori in "sola lettura" possono usare tutti gli stessi
puntatori, mentre gli altri hanno bisogno di essere copiati per ogni
thread, è corretto?
Grazie

 thread, è corretto?
Dipende da come usi i dati. Se piu' thread devono accedere
in scrittura agli stessi dati, allora puoi proteggerli usando
varie tecniche, tra cui mutex, semafori, condvar e altro.
Oppure puoi fare in modo che ogni thread abbia un set
di dati di out privati.
bb

 scontato, o no?
Direi proprio di si' :)
IL kernel schedulera' i thread, e li spalmera' su core diversi per distribuire il carico.
Pleg

 tecnica, rispetto al MT.
Puoi darmi qualche referenza? Non avevo mai sentito una tale preferenza verso il MP, ho sempre visto usare MT con liberalita'...
Ho cercato sul sito di Alex ma non ho trovato nulla a parte una slide con "non usate i thread in python!", non molto esplicativa :)
Pleg

> Ottenere il numero di cpu con pthread è possibile?
Mi par proprio di no. E non so nemmeno se ci sia una qualche chiamata di sistema per ottenere quel valore.
L'unica cosa che mi viene in mente, su linux, e' leggere /proc/cpuinfo, da li' puoi recuperare il numero di core.
Pleg

 bb
Dunque i miei thread non devono collaborare ma solo macinare, quindi ho
seguito l'approccio più stupido, crea le strutture e usane una copia per
thread, questo è quello che ho prodotto finora
--8<---------------cut here---------------start------------->8---
int main(int argc, char *argv[])
{
     int i, rc;
     pthread_attr_t attr;
     void *status;
     init_hand(&game_conf.hand_init);
     get_args(argc, argv, &game_conf);
     Game game_threads[NUM_THREADS];
     pthread_t thread[NUM_THREADS];
 
     for (i = 0; i < NUM_THREADS; i++) {
          // simple approach, just copy the whole structure every time
          game_threads[i] = game_conf;
          game_threads[i].index = i;
          pthread_create(&thread[i], &attr, loop, (void *) game_threads[i]); 
     }
     for (i = 0; i < NUM_THREADS; i++) {
          pthread_join(thread[i], &status);
          output_result(game_threads[i].result);
          free_conf(&game_threads[i]);
     }
     return 0;
}
--8<---------------cut here---------------end--------------->8---
Questa linea si lamenta
--8<---------------cut here---------------start------------->8---
pthread_create(&thread[i], &attr, loop, (void *) game_threads[i]); 
--8<---------------cut here---------------end--------------->8---
--8<---------------cut here---------------start------------->8---
razz.c: In function ‘main’:
razz.c:48: error: cannot convert to a pointer type
razz.c:48: warning: passing argument 3 of ‘pthread_create’ from incompatible pointer type
--8<---------------cut here---------------end--------------->8---
Dove questo è il loop:
--8<---------------cut here---------------start------------->8---
void *
loop(Game *game) {
--8<---------------cut here---------------end--------------->8---
Tra l'altro non mi è chiara una cosa, uso void * come tipo così posso
tornare il mio valore direttamente in status, giusto?
Se a me non frega niente e acconsento a modificare le stutture che passo
e leggerne i valori che succede?
Tra l'altro definendo invece loop come
void *loop(void *game)
come potrei castare senza avere un altra variabile in ballo, questo
Game *game = (Game *) game;
non piace al compilatore...
Grazie a tutti

 non piace al compilatore...
Certo, stai usando lo stesso nome per due variabili diverse!
fai cosi:
Game* pGame = (Game*)game;
     pGame->index=0;
Oppure:
    ((Game*)(game))->index=0;
oppure:
#define pGAME(p)	((Game*)(p))
   pGAME(game)->index=0;
Se per evitare un warning devi infarcire il codice di recast,
allora e' meglio se usi una variabile di appoggio.
bb.

 bb.
Va benissimo la variabile d'appoggio, credevo semplicemente di poter
ridichiarare il tipo di una variabile nello scope di una funzione, ma in
effetti in C (giustamente) non si può...
Ok grazie mille ci siamo quasi, ma questo ancora non va
--8<---------------cut here---------------start------------->8---
     Game* pGame = (Game *)game;
     pGame->index = 0;
 
     // We use only ONE deck! Initialize it directly without the initial hand
     Deck *deck = pGame->deck;
--8<---------------cut here---------------end--------------->8---
--8<---------------cut here---------------start------------->8---
razz.c:115: error: incompatible types in initialization
--8<---------------cut here---------------end--------------->8---
E perché mai?
Ora pGame è un puntatore a struttura che contiene a sua volta un
puntatore a deck...
L'unica cosa è che se non ho capito male a pthread_exit devo passare
l'indice con cui li thread è stato creato, se lo resetto a 0 non va
bene...

 bene...
Come non detto errore stupido mio, il codice è sempre qui:
http://github.com/AndreaCrotti/Razz/blob/threads/razz.c
E non mi convince il fatto che i thread inizino e terminino nello stesso
ordine, mi puzza di mancanza di parallelismo.
Ma è giusto questo for?
Non è che la exit con index sempre a 0 influisca?
--8<---------------cut here---------------start------------->8---
for (i = 0; i < NUM_THREADS; i++) {
     pthread_join(thread[i], NULL);
     printf("ended thread %d\n", i);
     output_result(game_threads[i].result);
}
--8<---------------cut here---------------end--------------->8---

 bene...
Guarda che pGame->index = 0; l'ho messo solo per esempio!
A pthread_exit va passato il puntatore al valore da ritornare
in status (nel tuo caso), ma non ti dovrebbe servire!
la chiamata a pthread_exit dovebbe poi essere implicita e
basta il return della funzione...
Come sincronismo usi il pthread_join, ma puoi anche considerare
l' uso del pthread_barrier ;-)
bb

 bb
Ok, beh l'index l'avevo creato solo perché avevo capito male a cosa
servisse l'argomento a pthred_exit...
pthread_barrier ho paura non ci sia su osx, almeno il man non c'è.
Comunque ora mancherà sicuramente qualcosa e si può fare meglio, ma
tenendo conto che vengono in effetti usate tutte e 2 le CPU va il 30-40%
più lento della versione senza thread.
Dubito sia la copia di 2 piccole strutture dati iniziali a influire..

 Non è che la exit con index sempre a 0 influisca?
No ;-) e' che la pthtread_join attende (ed e' bloccata su)
un singolo thread.
Quindi e' ovvio che se attendi il termine dei thread
in sequenza 0,1,2,3, questo sara' l' ordine che vedi con
la printf, anche se termina prima il thread 3 del thread 0...
Se vuoi sapere l' ordine effettivo, puoi mettere
una printf prima del return di ogni singolo thread...
bb

 Dubito sia la copia di 2 piccole strutture dati iniziali a influire..
Nel tuo caso, con due CPU, dovresti almeno incrementare
i tempi di escuzione quasi del doppio, eseguendo due thread
in parallelo...
Non conosco osx, controlla la politicha di scheduling
usata (se round-robin, fifo, o altro). Se usa una politica tipo
fifo, puo' essere che i thread alla stessa priorita'
sono eseguiti ... in serie invece che in parallelo.
bb

 bb
Quello che pensavo anche io, ma siccome non ottengo più del doppio vuol
dire che in qualche modo sono eseguiti in parallelo.
Anche perché un thread non può lavorare su più di un processore giusto
(e ho visto l'utilizzo salire a 150-170%)

 (e ho visto l'utilizzo salire a 150-170%)
Che strano, con un solo thread va praticamente come la versione senza
pthread.
Da 2 in poi ha più o meno sempre le stesse prestazioni che sono peggiori
dell'altro, nonostante la cpu vada a tutta birra.
Non c'entra neanche la stampa in output, anche disabilitando è uguale.
Non credo sia la copia di una struttura da 52int il problema, che dite?
Siamo sicuri che il mio for sia giusto così?
--8<---------------cut here---------------start------------->8---
     int i;
     init_hand(&game_conf.hand_init);
     prepare_game(argc, argv, &game_conf);
     Game game_threads[NUM_THREADS];
     pthread_t thread[NUM_THREADS];
 
     for (i = 0; i < NUM_THREADS; i++) {
          // simple approach, just copy the whole structure every time
          game_threads[i] = game_conf;
          printf("startin thread %d\n", i);
          pthread_create(&thread[i], NULL, loop, (void *)&game_threads[i]); 
     }
     for (i = 0; i < NUM_THREADS; i++) {
          pthread_join(thread[i], NULL);
          printf("ended thread %d\n", i);
          output_result(game_threads[i].result);
     }


Ho fatto esperimenti su 2 o 3 macchine linux in teoria multicore e direi
che i risultati sono gli stessi se non peggiori..
Il profiling non aiuta, vedo sempre più o meno le stesse cose, come si
"debuggano" le prestazioni dei thread in generale?
Non è che sia indispensabile tra l'altro va già veloce abbastanza senza,
però mi scoccia...