Con risposta Thread paralleli con Win32 C++

  • domenica 18 marzo 2012 11:11
     
     

    Buongiorno,

    ho scritto un progettino in cui ci sono sostanzialmente 3 thread:

    - uno principale che aspetta messaggi su una porta UDP;

    - uno che cattura immagini da una telecamera;

    - uno che elabora le immagini prelevate, quando ce ne sono.

    Innanzitutto non ho ancora capito come far girare i thread su diversi core (ho un i7 quadcore).. idee?

    Nel frattempo, visto che l'elaborazione dell'immagine ci mette troppo tempo, pensavo di creare due sottothread che elaborano metà dell'immagine, in parallelo. Di qui la domanda:

    -come faccio a creare due thread ed essere sicuro che girano in parallelo, magari su due core diversi?

    Attualmente uso _beginthreadex.

    Grazie,

    Paolo

Tutte le risposte

  • domenica 18 marzo 2012 12:43
     
     Con risposta

    Paolo Piazzi wrote:

    Buongiorno,

    ho scritto un progettino in cui ci sono sostanzialmente 3 thread:

    - uno principale che aspetta messaggi su una porta UDP;

    - uno che cattura immagini da una telecamera;

    - uno che elabora le immagini prelevate, quando ce ne sono.

    Innanzitutto non ho ancora capito come far girare i thread su diversi core (ho un i7 quadcore).. idee?

    Come prima cosa non confondere le operazioni che sono I/O bound da quelle CPU bound.
    Se l'operazione usa delle completion port verso un device, in user mode non stai usando alcun thread aggiuntivo e di fatto non ce n'è neppure bisogno se la libreria è pensata per restituirti il controllo in una callback con il risultato atteso.

    Il discorso è totalmente diverso se c'è un effettivo impegno CPU, come per esempio un algoritmo che fa calcoli pesanti. In questo caso dovrai usare almeno un thread su cui scaricare il calcolo.

    Nel tuo esempio hai solo operazioni di I/O e quindi il discorso thread è sterile. Se la libreria è bloccante, il thread secondario viene solo utilizzato per attendere un risultato e quindi legarlo ad uno specifico core della CPU è totalmente inutile.
     > Nel frattempo, visto che l'elaborazione dell'immagine ci mette troppo tempo,

    pensavo di creare due sottothread che elaborano metà dell'immagine, in parallelo. Di qui la domanda:

    -come faccio a creare due thread ed essere sicuro che girano in parallelo, magari su due core diversi?

    Attualmente uso _beginthreadex.

    Grazie,

    Se parli di elaborazione immagine questa è ovviamente una faccenda di CPU e qui bisogna precisare che gestire manualmente i thread è sconventiente perché:
    - il codice non è più portabile tra piattaforme differenti
    - il tuo processo non ha e non dovrebbe mai conoscere il carico dei vari core del PC. Questo significa che se tu prendi delle decisioni sul core su cui far girare il thread, rischi di rallentare immensamente tutto quanto
    - se il codice che vai ad eseguire dovesse essere particolarmente rapido (per motivi che magari non puoi neppure conoscere a priori) rischi che la creazione e la distruzione del thread sia più onerosa del calcolo stesso.

    Per questo e altri motivi, a partire da VC++2010 hai a disposizione le PPL (Parallel Patterns Library)
    http://msdn.microsoft.com/en-us/concurrency/bb964701
    e
    http://msdn.microsoft.com/en-us/magazine/dd434652.aspx

    Il succo è quello di suddividere i blocchi del tuo codice in modo da isolare delle unit of work che siano il più possibili indipendenti tra di loro (cioè che l'esecuzione di uno non abbia dipendenze dagli altri).
    Per ciascuna di queste unit of work crei un task che rappresenta quella specifica elaborazione.
    I task a questo punto possono essere eseguiti in parallelo e le eventuali dipendenze possono essere stabilite creando le cossiddette "continuations" (esecuzioni in cascata).

    Le modalità di esecuzione verranno stabilite a runtime dalla libreria che prenderà decisioni importanti quali:
    - stabilire se ha senso creare effettivamente un thread per quell'operazione
    - utilizzare lo scheduler in user-mode (disponibile a partire da Win7) invece di thread kernel, a tutto vantaggio delle performance totali
    - work stealing. Ogni thread ha una coda che deve eseguire un certo numero di thread, ma l'eventuale svuotamento di una coda provoca il livellamento delle code in modo che ogni thread abbia sempre qualcosa da fare.
    - e molto molto altro ...
     La morale è che non dovresti mai prendere decisioni che dipendano strettamente dall'hardware sottostante.


    Raffaele Rialdi  http://www.iamraf.net
    Weblog: http://blogs.ugidotnet.org/raffaele
    Microsoft MVP profile https://mvp.support.microsoft.com/profile/raffaele
    UGIdotNET - http://www.ugidotnet.org/


    Raffaele Rialdi [MVP] My articles and videos: http://www.iamraf.net Italian blog: http://blogs.ugidotnet.org/raffaele
  • domenica 18 marzo 2012 13:43
     
     

    Innazitutto grazie mille della risposta velocissima e molto approfondita.

    Parto dalla tua ultima frase "La morale è che non dovresti mai prendere decisioni che dipendano strettamente dall'hardware sottostante."

    E' proprio questo il punto a cui volevo arrivare: io non ho intenzione di scendere così tanto di livello e dire chi deve svolgere cosa. Volevo solo capire se esiste un modo per forzare Windows a far girare il processo sui più core, magari avendo prima diviso in thread il mio programma. E' un po' quello che ci dev'essere alla base di openMP o cose del genere.

    La logica del mio programma è questa:

    - il thread di acquisizione dell'immagine detta i tempi. Questo perché so a priori che la camera mi rende disponibile un'immagine ogni 5ms. La acquisisco.

    - nell'altro threasd (quello di elaborazione) nel frattempo (cioè nel periodo di 5 ms) elaboro la precedente. 

    In questo modo non dovrei avere perdita di immagini, a patto che il thread di elaborazione non sfori i 5 ms. Visto che attualmente sfora i 5 ms (probabilmente l'elaborazione delle immagini non l'ho ottimizzata, essendo immagini su cui vengono fatte un sacco di calcoli), la mia idea era appunto di creare due thread e dargli in pasto mezza immagine ciascuno. L'importante è che l'esecuzione dei due thread non avvenga serialmente, altrimenti ho perso il vantaggio di dividere in due l'immagine (avendo complicato il codice) e torno daccapo.

    Per cui inizio a guardare i due link che mi hai mandato che mi sembrano molto interessanti, nel frattempo penso a come suddividere il lavoro in unità. A questo punto sembra quasi più vantaggioso lasciare l'immagine intera e suddividere in task che elaborano sottoporzioni di immagine e mi restituiscono il sottorisultato..

    Ancora grazie,

    Paolo

  • domenica 18 marzo 2012 14:08
     
     

    Paolo Piazzi wrote:

    Innazitutto grazie mille della risposta velocissima e molto approfondita.

    Prego


    Parto dalla tua ultima frase "La morale è che non dovresti mai prendere decisioni che dipendano strettamente dall'hardware sottostante."

    E' proprio questo il punto a cui volevo arrivare: io non ho intenzione di scendere così tanto di livello e dire chi deve svolgere cosa. Volevo solo capire se esiste un modo per forzare Windows a far girare il processo sui più core, magari avendo prima diviso in thread il mio programma. E' un po' quello che ci dev'essere alla base di openMP o cose del genere.

    Le PPL sono lì proprio per questo. La libreria può prendere decisioni più complesse di quanto normalmente una app dovrebbe fare e sono decisioni che variano a seconda dell'hardware (sono prese a runtime).
     > La logica del mio programma è questa:


    - il thread di acquisizione dell'immagine detta i tempi. Questo perché so a priori che la camera mi rende disponibile un'immagine ogni 5ms. La acquisisco.

    Questo è il classico pattern "future" cioè avvii un'operazione che ti fornirà un risultato asincrono.
     > - nell'altro threasd (quello di elaborazione) nel frattempo (cioè nel periodo

    di 5 ms) elaboro la precedente. 

    Al termine del future precedente, puoi avviare un task di elaborazione. Le PPL probabilmente lo scheduleranno in un thread separato che lavorerà su uno dei core 'scarichi' (non è rilevante dal punto di vista applicativo) e questo potrai notarlo anche sul task manager.
     > In questo modo non dovrei avere perdita di immagini, a patto che il thread di

    elaborazione non sfori i 5 ms. Visto che attualmente sfora i 5 ms (probabilmente l'elaborazione delle immagini non l'ho ottimizzata, essendo immagini su cui vengono fatte un sacco di calcoli), la mia idea era appunto di creare due thread e dargli in pasto mezza immagine ciascuno. L'importante è che l'esecuzione dei due thread non avvenga serialmente, altrimenti ho perso il vantaggio di dividere in due l'immagine (avendo complicato il codice) e torno daccapo.

    Ti può convenire misurare il tempo impiegato così da renderti conto come procedere. Se riesci a parallelizzare l'elaborazione dell'immagine, ne avrai comunque grossi benefici. Non è sempre banale e richiede di isolare le unit of work come dicevo nel post precedente.
     > Per cui inizio a guardare i due link che mi hai mandato che mi sembrano molto

    interessanti, nel frattempo penso a come suddividere il lavoro in unità. A questo punto sembra quasi più vantaggioso lasciare l'immagine intera e suddividere in task che elaborano sottoporzioni di immagine e mi restituiscono il sottorisultato..

    Esiste anche uno step successivo che è quello di usare le librerie+estensioni AMP che sono disponibili con la beta di VC++11.
    In sostanza hai a disposizione degli strumenti che ti permettono di scrivere la porzione di calcolo con alcune restrizioni rispetto al C++ classico.
    Il risultato è che quel codice di pura elaborazione potrà essere uploadato nella GPU ed elaborato molto più velocemente di qualsiasi CPU che tu abbia a disposizione.
    Ovviamente devi avere calcolo per cui valga la pena eseguire il marshalling in GPU.
    Se poi il risultato dell'elaborazione è destinato alla visualizzazione, puoi anche mostrarlo direttamente a video senza riscaricarlo nella RAM principale, a tutto vantaggio delle performance.

    Il primo passo cmq è quello di strutturare il codice in modo che sia utilizzabile con le PPL. Il mio suggerimento è di usare le lambda (standard C++11) che facilitano molto la struttura del codice.


    Ancora grazie,

    Prego, ciao


    Raffaele Rialdi  http://www.iamraf.net
    Weblog: http://blogs.ugidotnet.org/raffaele
    Microsoft MVP profile https://mvp.support.microsoft.com/profile/raffaele
    UGIdotNET - http://www.ugidotnet.org/


    Raffaele Rialdi [MVP] My articles and videos: http://www.iamraf.net Italian blog: http://blogs.ugidotnet.org/raffaele