DOMANDA Dividere il carico su threads

Yang.

Nuovo Utente
81
4
CPU
Ryzen 7 2700X
Dissipatore
Custom Water Loop Barrow/Bykski
Scheda Madre
Asus X470 Strix
HDD
Intel 600p 512Gb SSD / WD Black 2 TB
RAM
2x16 GB DDR4 Team Group Night Hawk RGB
GPU
XFX Radeon RX 580 GTS
Audio
Integrata
Monitor
2x Asus MG279Q
PSU
Corsair RM850i
Case
Modded Cooler Master MasterCase H500P
Periferiche
Logitech G910, G502, G920, G933, C920
Net
FTTH 1000/100 Mbps
OS
Windows 10 Pro
Ciao a tutti,
come da titolo ho una domanda, ovvero : "Come posso dividere il carico totale che il mio software ha sulla CPU in maniera piu omogenea?"
Scendendo piu nei dettagli : ho creato un piccolo progetto universitario con Unreal Engine, codificato in C++ tramite Visual Studio.
Il problema che riscontro però è che, nonostante il carico di lavoro sia piuttosto basso, questo non è per nulla suddiviso sui core.
Mi ritrovo con il "primo" thread che prende circa il 90-95% del carico.
E' un errore di coding che ho commesso io oppure dipende da altro? (es. Piattaforma di sviluppo, linguaggio, motore grafico).
Nel caso dipendesse dal codice, che modifiche dovrei apportare?
 

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,222
1,853
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Dovremmo vedere il codice per capire il motivo di quel carico. Ad esempio, c'è un loop principale che disegna una finestra o refresha il contenuto di una finestra?
Per quanto riguarda la suddivisione del lavoro, non è chiaro se fai uso di thread (se ho capito bene, no); se non ne utilizzi dovresti individuare quelle parti di codice che possono essere eseguite parallelamente ed utilizzare uno o più threads (di norma non si supera il numero che mette a disposizione l'hw). Non esagerare in quanto potresti ottenere l'effetto non sperato/desiderato (perdita di performance).
Considera comunque che lo scheduling non è sotto al tuo controllo, quindi i tuoi N threads potrebbero venir eseguiti tutti nello stesso istante t su cores differenti, come venir schedulati in momenti diversi.

Non entro però nello specifico di Unreal, in quanto non saprei essere di grande aiuto.
 
  • Mi piace
Reazioni: Yang.

Yang.

Nuovo Utente
81
4
CPU
Ryzen 7 2700X
Dissipatore
Custom Water Loop Barrow/Bykski
Scheda Madre
Asus X470 Strix
HDD
Intel 600p 512Gb SSD / WD Black 2 TB
RAM
2x16 GB DDR4 Team Group Night Hawk RGB
GPU
XFX Radeon RX 580 GTS
Audio
Integrata
Monitor
2x Asus MG279Q
PSU
Corsair RM850i
Case
Modded Cooler Master MasterCase H500P
Periferiche
Logitech G910, G502, G920, G933, C920
Net
FTTH 1000/100 Mbps
OS
Windows 10 Pro
Grazie mille per la risposta.
Comunque no, non ho usato thread (perché non avevo idea di cosa fossero), quindi è sicuramente questo il motivo del carico non ripartito.
Mi studio come funzionano e come implementarli e vedo di risolvere.
Mi sai per caso consigliare qualche sito/libro che tratti l'argomento?
 

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,222
1,853
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Tecnicamente non è complessa la creazione; scrivere del buon codice multithread invece è cosa complessa.

Puoi iniziare dal reference ufficiale comunque: http://www.cplusplus.com/reference/thread/thread/

Dovresti trovare info sulla sincronizzazione, l'uso dei semafori ed i mutex. Se guardi bene dovresti trovare tutto.

Comunque di norma sono trattati da tutti i testi che trattano uno specifico linguaggio. Quindi se hai studiato su un libro, li troverai quasi sicuramente. ;)

Inviato da ONEPLUS A5000 tramite App ufficiale di Tom\'s Hardware Italia Forum
 
  • Mi piace
Reazioni: Yang. e Andretti60

Andretti60

Utente Èlite
6,440
5,091
Il processo di programmazione a multi thread è complicatissimo, direi che sia la parte di informatica più complicata che esista. Non che sia difficile da capire per se, ma è estremamente difficile da implementare bene ed è facilissimo fare errori. Ecco perché in passato certi linguaggi di programmazione non era nemmeno possibile farlo. Ed evito a scendere nei dettagli, ci vuole un libro (tipo Guerra e Pace). Per esempio in C era possibile solo creare un nuovo processo, copia di quello attuale, mediante un "fork" e avere i due processi girare allo stesso tempo.

Per rispondere un po’ meglio alla tua domanda, non tutti i sistemi operativi danno all'utente pieno controllo dell'uso dei core a disposizione, specie in quelli multi users multi tasking. Il sistema operativo sa meglio di noi quello che succede e quindi spetta a lui dividere il peso dei vari thread nei vari core e CPU a disposizione, è ottimizzato proprio per quello. Cosa diversa per i sistemi embedded dove c’è solo un processo che gira, in quel caso SE (con la maiuscola) sappiamo che il nostro programma esegue due thread con esattamente lo stesso caso, ha senso distribuirlo su due core diversi.

Nel tuo caso, il tuo programma è evidentemente single threaded, ossia ha un solo thread, per cui continuerà a caricare solo una CPU (magari su più core). Ma questo è detto in maniera molto semplicistica.

In parole povere, non è compito del programmatore dividere il carico della CPU, così come non lo è per ottimizzare lo spazio disco, che viene fatto dal file system. Multi threading programming è affascinante, ma bisogna studiare bene altrimenti sono guai. Ci siamo passati tutti, credimi.
 
  • Mi piace
Reazioni: Yang.

Yang.

Nuovo Utente
81
4
CPU
Ryzen 7 2700X
Dissipatore
Custom Water Loop Barrow/Bykski
Scheda Madre
Asus X470 Strix
HDD
Intel 600p 512Gb SSD / WD Black 2 TB
RAM
2x16 GB DDR4 Team Group Night Hawk RGB
GPU
XFX Radeon RX 580 GTS
Audio
Integrata
Monitor
2x Asus MG279Q
PSU
Corsair RM850i
Case
Modded Cooler Master MasterCase H500P
Periferiche
Logitech G910, G502, G920, G933, C920
Net
FTTH 1000/100 Mbps
OS
Windows 10 Pro
Il processo di programmazione a multi thread è complicatissimo, direi che sia la parte di informatica più complicata che esista. Non che sia difficile da capire per se, ma è estremamente difficile da implementare bene ed è facilissimo fare errori. Ecco perché in passato certi linguaggi di programmazione non era nemmeno possibile farlo. Ed evito a scendere nei dettagli, ci vuole un libro (tipo Guerra e Pace). Per esempio in C era possibile solo creare un nuovo processo, copia di quello attuale, mediante un "fork" e avere i due processi girare allo stesso tempo.

Per rispondere un po’ meglio alla tua domanda, non tutti i sistemi operativi danno all'utente pieno controllo dell'uso dei core a disposizione, specie in quelli multi users multi tasking. Il sistema operativo sa meglio di noi quello che succede e quindi spetta a lui dividere il peso dei vari thread nei vari core e CPU a disposizione, è ottimizzato proprio per quello. Cosa diversa per i sistemi embedded dove c’è solo un processo che gira, in quel caso SE (con la maiuscola) sappiamo che il nostro programma esegue due thread con esattamente lo stesso caso, ha senso distribuirlo su due core diversi.

Nel tuo caso, il tuo programma è evidentemente single threaded, ossia ha un solo thread, per cui continuerà a caricare solo una CPU (magari su più core). Ma questo è detto in maniera molto semplicistica.

In parole povere, non è compito del programmatore dividere il carico della CPU, così come non lo è per ottimizzare lo spazio disco, che viene fatto dal file system. Multi threading programming è affascinante, ma bisogna studiare bene altrimenti sono guai. Ci siamo passati tutti, credimi.

Nel mio caso quindi è utile distribuire il carico su più threads oppure conviene lasciare il tutto single-threaded ? Perché se ho ben capito anche un solo thread può esser eseguito su più core, se il sistema operativo lo ritiene opportuno.
 

Andretti60

Utente Èlite
6,440
5,091
Esatto, è proprio lo scopo dei CPU multi core.
Tieni conto che una applicazione non "gira" più veloce se è multi threaded, in quanto ci vuole tempo per la CPU di invertire i task, è solo più conveniente per noi se vogliamo avere più task indipendenti che però possono "parlarsi" tra loro e scambiare dati (e qui viene il grosso problema, ma come ho già detto è complesso). Multi threading è essenziale se sappiamo che uno o più task (inteso come lavoro generico) richiede molto tempo di elaborazione e dovrà passare anche molto tempo non facendo nulla, aspettando per esempio la risposta da periferiche (tipico di operazioni I/O su disco). In quei momenti di "ozio", il sistema operativo potrà fare girare un altro dei nostri thread.
Per esempio è molto utile per sviluppare interfacce grafiche se dobbiamo compiere azioni lunghe che bloccherebbero completamente l'uso della applicazione stessa (tipo non possiamo nemmeno minimizzare la finestra). Molto meglio fare elaborazioni lunghe in un thread separato, che quando ha finito comunica i risultati al thread principale.
 

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,222
1,853
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Ti porto un altro esempio in aggiunta. Sorvolando su Vulkan, OpenGL etc. si può usare anche in contesti come l'elaborazione di immagini.
Immagina di avere un'immagine alla quale vuoi applicare un filtro: puoi suddividere l'immagine in quadranti (supponiamo 4) ed eseguire le parti separatamente così da elaborare in parallelo ciascuna parte, con un vantaggio sul tempo totale.

Ovviamente vale tutto quanto esposto già in precedenza: lo scheduling non è sotto al tuo controllo diretto.

L'esempio della GUI portato da Andretti è un classico, altrimenti avresti la GUI congelata.

Questo è uno dei classici problemi che si affronta anche in fase di studio; potresti dargli un occhio, così da capire meglio alcuni degli elementi coinvolti.

https://en.m.wikipedia.org/wiki/Producer–consumer_problem

Inviato da ONEPLUS A5000 tramite App ufficiale di Tom\'s Hardware Italia Forum
 
  • Mi piace
Reazioni: Yang. e Andretti60

Andretti60

Utente Èlite
6,440
5,091
Direi che la pagina wiki esposta dal nostro amico DispatchCode sia un buon esempio per capire almeno i punti cruciali della programmazione multi threading. Ovviamente è solo l'inizio. La parola chiave qui è "sincronizzazione", ossia spetta a noi fare in modo che i vari thread possano comunicare senza bloccarsi a vicenda. Esistono vari metodi, non tutti sono presenti in tutti i linguaggi, e la tecnica migliore dipende dal tipo di applicazione. Per esempio, ben diverso il caso di un thread che appena finito il compito lo comunica al thread principale e poi chiude, con il caso di tanti thread che sono sempre attivi e il cui lavoro dipende dallo stato degli thread. Appena due thread vanno in blocco, si pianta tutto.
 

pabloski

Utente Èlite
2,868
916
Il processo di programmazione a multi thread è complicatissimo, direi che sia la parte di informatica più complicata che esista. Non che sia difficile da capire per se, ma è estremamente difficile da implementare bene ed è facilissimo fare errori.

E poi la gente mi chiede stranita perchè ho adottato Go dal day one :D

p.s. comunque credo che dal discorso in oggetto non si possa escludere Unreal. Non so come funziona ( se come una libreria o un framework ), ma credo che l'engine dovrebbe prevedere una quale forma di gestione della concorrenza/parallelismo. In fondo è un qualcosa di necessario per i giochi, vista quanta potenza bruta richiedono.

Per esempio ho trovato questo https://wiki.unrealengine.com/Multi-Threading:_How_to_Create_Threads_in_UE4
 

Andretti60

Utente Èlite
6,440
5,091

Un buon articolo, ma intacca solo la superficie.
Creare e gestire thread è facile in quasi tutti i nuovi linguaggi di programmazione, incluso i motori usati nei video giochi come appunto unreal engine. Farlo bene? Non molto. L'articolo stesso verso la fine ammonisce dei rischi che si corrono. Per esempio ricorda di non eseguire nessuna grafica in un Working thread, gli oggetti grafici possono venire manipolati solo nel thread che li ha generati (chi lavora per GUI in .net la parola più frequente che usa è "Invoke" ) ;)
 

pabloski

Utente Èlite
2,868
916
Creare e gestire thread è facile in quasi tutti i nuovi linguaggi di programmazione, incluso i motori usati nei video giochi come appunto unreal engine. Farlo bene? Non molto.

Si, ovviamente non volevo entrare nel merito delle difficoltà relative alla programmazione concorrente, che era ed è una rogna senza fine. Se qualcuno ha sentito il bisogno di creare nuovi modelli di concorrenza ( il citato Go appunto ) è proprio perchè il multithreading/multiprocessing è difficile e prono agli errori.

Ho postato quell'articolo come spunto per cercare di capire come viene gestita la concorrenza i Unreal. Cioè, il programmatore creare degli oggetti che poi vengono gestiti ( con tanto di concorrenza ) dal motore grafico o è una cosa più manuale, per cui è il programmatore stesso a doverlo fare? Unity segue il primo approccio. E Unreal?

Cioè mi è parso strano che l'op chiudesse come lui deve farci entrare le concorrenza nel suo programma. L'engine non la gestisce? Non fa nulla? Cioè è praticamente una raccolta di funzioni ( magari nemmeno rientranti )?

chi lavora per GUI in .net la parola più frequente che usa è "Invoke" ) ;)

Perciò la gente sta migrando ad Electron e soci :D

Non che siano delle stelle luminose!
 
  • Mi piace
Reazioni: Andretti60

Andretti60

Utente Èlite
6,440
5,091
... Perciò la gente sta migrando ad Electron e soci :D
Non conosco Eletron.
Di fatto, C# e' uno dei languaggi da me provati che fa ha un ottimo lavoro per integrare l'uso di working thread, che sta migliorabdo con il tempo (sto usando in framework 4.0 che ' un abisso dal lontano 1.0). Vedi per esempio l'uso dai Task invece dei Thread.
Ma indipendente dal linguaggio e dal framework che si usa, il problema della sincronizzazione rimane sempre a carico del programmatore. Tutto sommato e' sempre meglio avere una applicazione a singolo thread, che funzioni bene anche se a volte irritante, che una multi threaded che si pianti in continuazione.
 

Andretti60

Utente Èlite
6,440
5,091
Tutto sommato, era piu semplice quando i thread si implementavano a mano, avendo una collezione di "task" che venivano chiamate a catena una dopo l'altra, ognuna occupando la CPU non piu' di mezzo secondo o meno, implementando le funzion con una "state machine" in modo da riprendere il lavoro dove lo aveva interrotto. Nessun problema di sincronizzazione, ovviamente non si guadagnava in tempo di elaborazione, in quanto non si puo' sfruttare i tempo morti, ed era possible per l'utente interrompere l'elaborazione e avere sempre un programma "responsivo". Tecnica che usavo sempre in Visual Basic dove non ci sono thread, alcuni di quei miei programmi girano ancora adesso!
 

pabloski

Utente Èlite
2,868
916
Tutto sommato, era piu semplice quando i thread si implementavano a mano, avendo una collezione di "task" che venivano chiamate a catena una dopo l'altra, ognuna occupando la CPU non piu' di mezzo secondo o meno, implementando le funzion con una "state machine" in modo da riprendere il lavoro dove lo aveva interrotto.

Lo fa il modello CSP usato da Go. Ovviamente usando più thread/core sono necessari accorgimenti come l'uso di strutture dati immutabili e tutto quello che ne consegue.

Sostanzialmente la concorrenza è difficile perchè la si è approcciata male. Per fortuna si sta correggendo il tiro con i nuovi linguaggi tipo Haskell, Rust, Go, Elixir, ecc...
 

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!