Perché un programma richiede un numero minimo specifico di core CPU?

55

È possibile scrivere codice (o software completo, piuttosto che un pezzo di codice) che non funzionerà correttamente quando viene eseguito su una CPU con meno di N numero di core? Senza controllandolo esplicitamente e fallendo di proposito:

IF (noOfCores < 4) THEN non funziona correttamente di proposito

Sto osservando un minimo di gioco ( Dragon Age: Inquisition ) requisiti di sistema e indica un minimo di una CPU a quattro core. Molti giocatori dicono che NON funziona su CPU a due core e EVEN su Intel Core i3 con due core fisici e due logici. E NON è un problema di potenza di calcolo.

Da quanto ho capito, i thread sono completamente isolati dalla CPU dal sistema operativo poiché ciò non può essere fatto.

Solo per chiarire le cose:

I am NOT chiede "Posso scoprire il numero di core CPU dal codice e fallire di proposito?" ... Tale codice sarebbe malintenzionato (ti costringe ad acquistare una CPU più costosa per eseguire un programma - senza bisogno di potenza di calcolo). Sto chiedendo che il tuo codice, ad esempio, abbia quattro thread e fallisca quando due thread vengono eseguiti sullo stesso core fisico (senza controllare esplicitamente le informazioni di sistema e fallendo di proposito) .

In breve, può esistere un software che richiede più core, senza richiedere ulteriore potenza di calcolo proveniente da più core? Richiederebbe solo N core fisici separati.

    
posta Reek 07.01.2015 - 13:12
fonte

12 risposte

45

Potrebbe essere possibile farlo "per caso" con l'uso imprudente dell'affinità di base. Considera il seguente pseudocodice:

  • avvia una discussione
  • in quel thread, scopri quale core è in esecuzione su
  • imposta la sua affinità CPU con quel core
  • inizia a fare qualcosa di computazionalmente intenso / loop per sempre

Se si avviano quattro di questi su una CPU a due core, allora qualcosa va storto con l'impostazione di affinità principale o si finisce con due thread che eseguono il hogging dei core disponibili e due thread che non vengono mai pianificati. In nessun momento ha chiesto esplicitamente quanti nuclei ci sono in totale.

(Se hai thread con esecuzione prolungata, l'impostazione dell'affinità della CPU in genere migliora il throughput)

L'idea che le società di gioco stiano "costringendo" le persone ad acquistare hardware più costoso senza una buona ragione non è molto plausibile. Può solo perdere clienti.

Modifica: questo post ha ora 33 upvotes, il che è un bel po 'dato che è basato su congetture educate!

Sembra che le persone abbiano ottenuto DA: I, per funzionare male, su sistemi dual-core: link Questa analisi indica che la situazione migliora notevolmente se viene attivato l'hyperthreading. Dato che HT non aggiunge ulteriori unità di problema o cache, consente semplicemente l'esecuzione di un thread mentre un altro è in uno stallo della cache, il che suggerisce strongmente che è collegato esclusivamente al numero di thread.

Un altro poster afferma che la modifica dei driver grafici funziona: link ; dato che i driver grafici tendono ad essere un misero alveare di feccia e villania, questo non è sorprendente. Un famigerato set di driver aveva una modalità "corretta & lenta" rispetto alla "fast & errata" che era stata selezionata se chiamata da QUAKE.EXE. È del tutto possibile che i driver si comportino diversamente per diversi numeri di CPU apparenti. Forse (tornando alla speculazione) viene utilizzato un diverso meccanismo di sincronizzazione. Uso improprio di spinlock ?

"L'uso improprio delle primitive di chiusura e sincronizzazione" è una fonte molto comune di bug. (Il bug che dovrei guardare sul lavoro mentre scrivo questo è "crash se si cambiano le impostazioni della stampante contemporaneamente al termine del lavoro di stampa").

Modifica 2: commenti menzionano il sistema operativo che tenta di evitare l'inattività del thread. Si noti che il gioco può avere il proprio quasi-scheduler interno per l'assegnazione del lavoro ai thread, e ci sarà un meccanismo simile nella scheda grafica stessa (che è effettivamente un sistema multitasking a sé stante). Le probabilità di un bug in uno di questi o l'interazione tra di loro sono piuttosto elevate.

www.ecsl.cs.sunysb.edu/tr/ashok.pdf (2008) è una tesi di laurea sulla migliore pianificazione per le schede grafiche che menziona esplicitamente che normalmente usano la pianificazione primo arrivato, primo servito, che è facile implementare in sistemi non preventivi. La situazione è migliorata? Probabilmente no.

    
risposta data 07.01.2015 - 17:01
fonte
34

Potrebbe essere necessario avere 4 core perché l'applicazione esegue quattro attività in thread paralleli e si aspetta che finiscano quasi contemporaneamente.

Quando ogni thread viene eseguito da un core separato e tutti i thread hanno lo stesso carico di lavoro computazionale, è molto probabile (ma lungi dall'essere garantito) terminare all'incirca nello stesso tempo. Ma quando due thread vengono eseguiti su un core, il timing sarà molto meno prevedibile perché il core cambierà continuamente il contesto tra i due thread.

I bug che si verificano a causa di tempi di thread imprevisti sono indicati come " condizioni di gara ".

Nel contesto dello sviluppo del gioco, un'architettura plausibile con questo tipo di problema potrebbe essere quella in cui diverse caratteristiche del gioco sono simulate in tempo reale da diversi thread della CPU. Quando ciascuna funzione viene eseguita su un core, vengono simulati all'incirca alla stessa velocità. Ma quando due feature vengono eseguite su un core, entrambe verranno simulate solo a metà della velocità del resto del mondo di gioco, il che potrebbe causare tutti i tipi di comportamenti strani.

Si noti che un'architettura software che dipende da thread indipendenti in esecuzione con tempistiche specifiche è estremamente fragile e un segno di pessima comprensione della programmazione concorrente. Ci sono funzioni disponibili praticamente in tutte le API di multithreading per sincronizzare esplicitamente i thread per prevenire questo tipo di problemi.

    
risposta data 07.01.2015 - 13:56
fonte
16

È improbabile che questi "requisiti minimi" rappresentino qualcosa sotto il quale il gioco non verrà eseguito. Molto più probabile è che rappresentino qualcosa sotto il quale il gioco non verrà eseguito con prestazioni accettabili. Nessuna azienda di giochi vuole trattare con un sacco di clienti che si lamentano di prestazioni scadenti quando la eseguono su una singola scatola centrale da 1 Ghz, anche se il software potrebbe funzionare tecnicamente. Quindi probabilmente progettano deliberatamente di fallire su scatole con meno core di quelle che darebbero loro prestazioni accettabili.

Una metrica importante nelle prestazioni del gioco è la frequenza fotogrammi. Tipicamente corrono a 30 o 60 fotogrammi al secondo. Ciò significa che il motore di gioco deve rendere la visualizzazione corrente dallo stato di gioco in un intervallo di tempo prestabilito. Per ottenere 60 fps, ha solo un po 'più di 16 msec per farlo. I giochi con grafica high-end sono estremamente limitati dalla CPU e quindi c'è un enorme consenso tra il tentativo di spingere verso una maggiore qualità (che richiede più tempo) e la necessità di rimanere in questo budget. Pertanto, il budget di tempo per ogni frame è estremamente stretto.

Poiché il budget temporale è limitato, lo sviluppatore preferisce l'accesso esclusivo a uno o più core. Probabilmente vogliono anche essere in grado di fare le loro cose di rendering in un core, in esclusiva, dato che è ciò che deve essere fatto in quel budget, mentre altre cose, come il calcolo dello stato mondiale, avvengono in un processo separato dove non intromettersi.

Potresti, in teoria, stipare tutto questo su un singolo core, ma poi tutto diventa molto più difficile. All'improvviso devi assicurarti che tutta quella roba sullo stato del gioco sia abbastanza veloce e che il tuo rendering possa accadere. Non puoi semplicemente renderli due thread software perché non c'è modo di far capire al sistema operativo "thread A deve completare X quantità di lavoro in 16 msec indipendentemente da quale thread B fa".

Gli sviluppatori di giochi non hanno interesse a farti comprare nuovo hardware. La ragione per cui hanno i requisiti di sistema è che il costo del supporto delle macchine di fascia bassa non vale la pena.

    
risposta data 07.01.2015 - 21:44
fonte
9

Tre thread in tempo reale che non dormono mai e un altro thread. Se ci sono meno di quattro core, il quarto thread non viene mai eseguito. Se il quarto thread deve comunicare con uno dei thread in tempo reale per il thread in tempo reale, il codice non terminerà con meno di quattro core.

Ovviamente se i thread in tempo reale sono in attesa di qualcosa che non consente loro di addormentarsi (come uno spinlock), il progettista del programma ha sbagliato.

    
risposta data 07.01.2015 - 19:31
fonte
3

Prima di tutto i thread software non hanno nulla a che fare con i thread hardware e sono spesso confusi. I thread software sono pezzi di codice che possono essere inviati ed eseguiti autonomamente nel contesto del processo. I thread hardware sono per lo più gestiti dal sistema operativo e sono inviati al core del processore quando si parla di programmi regolari. Questi thread hardware vengono inviati in base al carico; il dispatcher del thread hardware agisce più o meno come un load balancer.

Tuttavia, quando si tratta di giochi, in particolare di giochi di fascia alta, a volte i thread hardware sono gestiti dal gioco stesso o il gioco indica al dispatcher di thread hardware cosa fare. Questo perché ogni attività o gruppo di attività non ha la stessa priorità come in un normale programma. Poiché Dragon Age proviene da uno studio di gioco di fascia alta che utilizza motori di gioco di fascia alta, posso immaginare che utilizzi il dispatch "manuale" e quindi il numero di core diventa un requisito di sistema minimo. Qualsiasi programma si arresterebbe in modo anomalo quando invio un pezzo di codice al terzo core fisico in esecuzione su una macchina con solo 1 o 2 core.

    
risposta data 07.01.2015 - 14:09
fonte
1

Poiché è possibile utilizzare virtualize per avere più core virtuali che fisici e il software non saprebbe che è in esecuzione su una virtualizzazione e invece pensa che abbia così tanti core fisici, direi che tale software non è possibile.

Cioè, non è possibile scrivere software che si fermerà sempre con meno di N core.

Come altri hanno sottolineato, ci sono soluzioni software che possono potenzialmente controllare, specialmente se il sistema operativo e il codice in uso hanno poca protezione contro le condizioni di gara quando i processi N sono eseguiti su processori < N. Il vero trucco è il codice che fallirà quando hai meno di N processori ma non fallirà quando hai processori N ma hai un sistema operativo che può assegnare lavoro a meno di processori N.

    
risposta data 07.01.2015 - 15:45
fonte
1

Potrebbe essere che ci siano tre thread che fanno qualcosa (generare sfondi o generare movimento NPC) e passare eventi a un quarto, che dovrebbe aggregare / filtrare gli eventi e aggiornare il modello di vista. Se il quarto thread non riceve tutti gli eventi (perché non è pianificato su un core), il modello di vista non viene aggiornato correttamente. Questo può accadere solo sporadicamente, ma questi core devono essere disponibili in qualsiasi momento. Questo potrebbe spiegare perché non stai vedendo un elevato utilizzo della CPU tutto il tempo, ma il gioco non funziona correttamente comunque.

    
risposta data 07.01.2015 - 16:49
fonte
1

Penso che Joshua stia andando giù per la strada giusta, non solo per la sua conclusione.

Supponiamo di avere un'architettura in cui ci sono tre thread che sono stati scritti per fare quanto possono - quando finiscono ciò che stanno facendo lo fanno di nuovo. Per mantenere le prestazioni su questi thread non rilasciare il controllo per nulla - non vogliono rischiare il ritardo dall'utilità di pianificazione di Windows. Finché ci sono 4 o più core questo funziona bene, fallisce male se non ci sono.

In generale, questa sarebbe una pessima programmazione, ma i giochi sono un'altra cosa: quando ci si trova di fronte a una scelta tra un design che è inferiore su tutto l'hardware o un design che è superiore su hardware sufficientemente buono o un errore su un gioco hardware inferiore gli sviluppatori di solito scelgono di richiedere l'hardware.

    
risposta data 08.01.2015 - 05:46
fonte
1

Is it possible to write code (or complete software, rather than a piece of code) that won't work properly when run on a CPU that has less than N number of cores?

Assolutamente. L'uso di thread in tempo reale sarebbe un buon esempio di una situazione in cui questo è, non solo possibile, ma il modo desiderato (e spesso, l'unico modo corretto) per portare a termine il lavoro. Tuttavia, i thread in tempo reale sono generalmente limitati al kernel del sistema operativo, solitamente per i driver che devono essere in grado di garantire che un evento hardware di qualche tipo venga gestito entro un determinato periodo di tempo. Non si dovrebbero avere thread in tempo reale nelle normali applicazioni utente e non sono sicuro che sia possibile averne uno in un'applicazione in modalità utente di Windows. In generale, i sistemi operativi rendono intenzionalmente impossibile farlo dal punto di vista dell'utente, proprio perché consente a una determinata applicazione di assumere il controllo del sistema.

Per quanto riguarda le applicazioni utente-terra: la tua ipotesi che il controllo di un determinato numero di thread per l'esecuzione sia necessariamente intenzionale nell'intento non è corretto. Ad esempio, potresti avere 2 attività di lunga durata e ad alte prestazioni che hanno bisogno di un core. Indipendentemente dalla velocità del core della CPU, la condivisione di un core con altri thread potrebbe essere un degrado delle prestazioni grave e inaccettabile a causa del thrashing della cache insieme alle normali penalizzazioni causate dal cambio di thread (che sono piuttosto sostanziali.) In questo caso, sarebbe perfettamente ragionevole, soprattutto per un gioco, per impostare ciascuno di questi thread per avere un'affinità solo su un particolare core per ciascuno di essi e quindi impostare tutti gli altri thread in modo che non abbiano affinità su quei 2 core. Per fare ciò, però, dovresti aggiungere un controllo che il sistema abbia più di 2 core e fallire se non lo fa.

    
risposta data 08.01.2015 - 18:21
fonte
1

Qualsiasi codice che utilizza spinlock con una quantità notevole di conflitto di blocco si comporta in modo terribile (in una misura in cui, per un'applicazione come un gioco, è possibile dire "non funziona" ) se il numero di thread supera il numero di core.

Immagina per esempio un thread di produzione che invii attività a una coda che serve 4 thread di consumo. Ci sono solo due core:

Il produttore cerca di ottenere lo spinlock, ma è detenuto da un consumatore in esecuzione sull'altro core. I due core sono in esecuzione mentre il produttore sta girando, aspettando che il blocco venga rilasciato. Questo è già male, ma non così male come otterrà.
Sfortunatamente, il thread del consumatore è alla fine del suo tempo quantico, quindi viene preventivato e viene programmata un'altra discussione del consumatore. Cerca di ottenere il blocco, ma ovviamente il blocco viene eseguito, quindi ora due core stanno girando e aspettando qualcosa che non può accadere.
Il thread del produttore raggiunge la fine della sua fascia oraria e viene azzerato, un altro consumatore si sveglia. Ancora una volta, due consumatori stanno aspettando il rilascio di un lock, e non accadrà prima che siano passati altri due quantum.
[...] Finalmente il consumatore che teneva lo spinlock ha rilasciato la serratura. Viene immediatamente preso da chiunque giri nell'altro core. C'è una probabilità del 75% (3 a 1) che sia un altro thread di consumo. In altre parole, è del 75% probabile che il produttore sia ancora in fase di stallo. Ovviamente questo significa che anche i consumatori si bloccano. Senza le attività di produzione del produttore, non hanno nulla da fare.

Nota che questo funziona in linea di principio con qualsiasi tipo di blocco, non solo gli spinlock, ma l'effetto devastante è molto più importante con gli spinlock perché la CPU continua a bruciare i cicli senza ottenere nulla.

Ora immagina che oltre a quanto sopra alcuni programmatori avessero la brillante idea di usare un thread dedicato con affinità impostato sul primo core, così RDTSC darà risultati affidabili su tutti i processori (non lo farà comunque, ma alcune persone pensano così).

    
risposta data 08.01.2015 - 19:15
fonte
-1

Se capisco cosa stai chiedendo, è possibile, ma è una cosa molto, molto brutta.

L'esempio canonico di ciò che stai descrivendo manterrebbe un contatore che viene incrementato da più thread. Ciò non richiede quasi nulla in termini di potenza di calcolo, ma richiede un'attenta coordinazione tra i thread. Finché solo un thread alla volta esegue un incremento (che in realtà è una lettura seguita da un'aggiunta seguita da una scrittura), il suo valore sarà sempre corretto. Questo perché un thread legge sempre il valore "precedente" corretto, ne aggiunge uno e scrive il valore "successivo" corretto. Ottieni due thread contemporaneamente nell'azione e entrambi leggeranno lo stesso valore "precedente", ottengono lo stesso risultato dall'incremento e scrivono lo stesso valore "successivo". Il contatore sarà effettivamente stato incrementato solo una volta, anche se due thread pensano che ognuno di essi sia riuscito a farlo.

Questa dipendenza tra tempistica e correttezza è ciò che l'informatica definisce una condizione di razza .

Le condizioni di gara sono spesso evitate usando meccanismi di sincronizzazione per assicurarsi che i thread che vogliono operare su un dato condiviso debbano mettersi in coda per l'accesso. Il contatore sopra descritto potrebbe utilizzare un blocco di lettura / scrittura per questo.

Senza l'accesso al design interno di Dragon Age: Inquisition , chiunque può fare è speculare sul perché si comporta come fa. Ma mi baserò su alcune cose che ho visto fare nella mia esperienza:

Potrebbe darsi che il programma sia basato su quattro thread che sono stati ottimizzati in modo che tutto funzioni quando i thread vengono eseguiti principalmente, senza interruzioni sui propri core fisici. Il "tuning" potrebbe venire sotto forma di riarrangiamento del codice o inserimento di posti letto in punti strategici per mitigare i bug indotti dalle condizioni di competizione che si sono verificati durante lo sviluppo. Ancora una volta, questa è tutta una congettura, ma ho visto che le condizioni di gara "si sono risolte" in questo modo più volte di quanto mi interessi contare.

L'esecuzione di un programma in questo modo su qualcosa di meno capace rispetto all'ambiente per il quale è stata sintonizzata introduce cambiamenti di temporizzazione dovuti al fatto che il codice non viene eseguito con rapidità o, più probabilmente, con i selettori di contesto. I commutatori di contesto avvengono in modo fisico (ovvero, i core fisici della CPU stanno passando tra il lavoro in cui i core logici sono in attesa) e logici (ovvero, il sistema operativo sulla CPU assegna il lavoro ai core), ma entrambi sono significativamente divergenti da ciò sarebbe il tempo di esecuzione "previsto". Ciò può far emergere un cattivo comportamento.

Se Dragon Age: Inquisition non fa il semplice passo per assicurarsi che ci siano abbastanza core fisici disponibili prima di procedere, è colpa di EA. Probabilmente stanno spendendo una piccola fortuna per le chiamate di supporto e le e-mail da parte di persone che hanno tentato di eseguire il gioco su un hardware troppo piccolo.

    
risposta data 07.01.2015 - 17:17
fonte
-1

Windows ha una funzionalità integrata per questo: la funzione GetLogicalProcessorInformation si trova nella API di Windows. Puoi chiamarlo dal tuo programma per ottenere informazioni su core, core virtuali e hyperthreading.

Quindi la risposta alla tua domanda sarebbe: Sì.

    
risposta data 07.01.2015 - 13:25
fonte

Leggi altre domande sui tag