Domanda

Ho giocato con la libreria parallela in .NET 4.0. Di recente, ho sviluppato un ORM personalizzato per alcune insolite operazioni di lettura/scrittura che uno dei nostri grandi sistemi deve utilizzare. Questo mi permette di decorare un oggetto con attributi e avere riflesso per capire quali colonne deve estrarre dal database, nonché di ciò che XML ha su cui produrre le scritture.

Dal momento che immagino che questo wrapper venga riutilizzato in molti progetti, vorrei spremere più velocità possibile. Questa libreria verrà utilizzata principalmente nelle applicazioni Web .NET. Sto testando il framework usando un'applicazione console usa e getta per colpire le classi che ho creato.

Ora ho imparato una lezione del sovraccarico con cui arriva il multithreading. Il multithreading lo fa funzionare più lentamente. Dalla lettura in giro, sembra che sia intuitivo per le persone che lo fanno da molto tempo, ma in realtà è controintuitivo per me: come può eseguire un metodo 30 volte allo stesso tempo Sii più lento che eseguirlo 30 volte in sequenza?

Non credo che sto causando problemi da più thread che devono combattere sullo stesso oggetto condiviso (anche se non sono ancora abbastanza bravo per dirlo con certezza o no), quindi presumo che il rallentamento provenga dall'alto Di generare tutti quei thread e il tempo di esecuzione mantenendoli tutti dritti. Così:

  • Anche se lo sto facendo principalmente come esercizio di apprendimento, è questa pessimizzazione? Per attività banali e nono, è un'overkill multithreading? Il mio obiettivo principale è la velocità, non la reattività dell'interfaccia utente o altro.
  • L'esecuzione dello stesso codice multithreading in IIS lo farà accelerare a causa di thread già creati nel pool di thread, mentre in questo momento sto usando un'app di console, che presumo che sarebbe stato singolo fino a quando non l'ho detto altrimenti? Sto per eseguire alcuni test, ma immagino che ci sia una conoscenza di base che mi manca per sapere perché Sarebbe un modo o l'altro. La mia app di console è anche in esecuzione sul mio desktop con due core, mentre un server per un'app Web ne avrebbe di più, quindi potrei doverlo usarlo anche come variabile.
È stato utile?

Soluzione

I thread in realtà non sono tutti correnti contemporaneamente.

Su una macchina desktop presumi che tu abbia una CPU a doppia core, (forse al massimo un quad). Ciò significa che solo 2/4 thread possono essere in esecuzione contemporaneamente.

Se hai generato 30 thread, il sistema operativo dovrà passare al contesto tra quei 30 thread per mantenerli tutti in funzione. Gli interruttori di contesto sono abbastanza costosi, quindi il rallentamento.

Come suggerimento di base, mirerei a 1 thread per CPU se stai cercando di ottimizzare i calcoli. Non più di questo e non stai davvero facendo alcun lavoro extra, stai solo scambiando i thread in uno stesso CPU. Prova a pensare al tuo computer come a un numero limitato di lavoratori all'interno, non puoi fare più lavoro contemporaneamente rispetto al numero di lavoratori che hai a disposizione.

Alcune delle nuove funzionalità nella libreria di attività parallele .NET 4.0 ti consentono di fare le cose che spiegano la scalabilità nel numero di thread. Ad esempio, puoi creare un sacco di attività e la libreria parallela di attività capirà internamente quanti CPU hai a disposizione e ottimizzare il numero di thread è crea/usa in modo da non sovraccaricare le CPU, in modo da poter creare 30 compiti, Ma su una macchina a doppia core la libreria TP creerebbe ancora solo 2 thread e avrebbe messo in coda il. Ovviamente, questo si ridimensionerà molto bene quando puoi eseguirlo su una macchina più grande. Oppure puoi usare qualcosa di simile ThreadPool.QueueUserWorkItem(...) Per mettere in coda un sacco di attività e il pool gestirà automaticamente quanti thread è utilizzato per eseguire tali attività.

Sì, c'è un sacco di sovraccarico da thread creazione, ma se si utilizza il pool di thread .NET (o la libreria di attività parallele in 4.0) .NET gestirà la creazione della tua discussione e potresti effettivamente scoprire che crea meno thread di Il numero di attività che hai creato. Sosterrà internamente le tue attività sui thread disponibili. Se si desidera effettivamente controllare la creazione esplicita di thread reali, è necessario utilizzare la classe thread.

Alcune CPU possono fare cose intelligenti con thread e possono avere più thread in esecuzione per CPU - vedere hyperthreading - ma controlla il tuo task manager, sarei molto sorpreso se hai più di 4-8 CPU virtuali sui desktop di oggi

Altri suggerimenti

Ci sono così tanti problemi con questo che vale per capire cosa sta succedendo sotto le copertine. Consiglio vivamente il libro "Programmazione simultanea su Windows" di Joe Duffy e il libro "Java Confulcy in Practice". Quest'ultimo parla dell'architettura del processore al livello necessario per capirla quando si scrive codice multithread. Un problema che colpirai che danneggerà il tuo codice è la memorizzazione nella cache, o più probabilmente la mancanza.

Come è stato affermato che c'è un sovraccarico per la pianificazione e l'esecuzione di thread, ma potresti scoprire che esiste un sovraccarico più ampio quando si condividono dati tra i thread. Tali dati possono essere scaricati dalla cache del processore nella memoria principale e ciò causerà gravi rallentamenti al codice.

Questo è il tipo di cose di basso livello da cui gli ambienti gestiti dovrebbero proteggerci, tuttavia, quando si scrive un codice altamente parallelo, questo è esattamente il tipo di problema che devi affrontare.

Un mio collega ha registrato uno screencast sul problema delle prestazioni con parallelo. Per e parallelo.

http://rocksolidknowledge.com/screencasts.mvc/watch?video=parallelloops.wmv

Stai parlando di un ORM, quindi presumo un po 'di I/O. In tal caso, il sovraccarico della creazione di thread e della commutazione del contesto sarà relativamente inesistente.

Molto probabilmente, stai vivendo una tesi I/O: può essere più lento (in particolare su dischi rigidi rotazionali, ma anche su altri dispositivi di archiviazione) leggere lo stesso set di dati se li leggi fuori servizio rispetto a se lo leggi -ordine. Quindi, se stai eseguendo 30 query di database, è possibile che funzionano più velocemente sequenzialmente che in parallelo se sono tutti supportati dallo stesso dispositivo I/O e le query non sono nella cache. Eseguirli in parallelo può far sì che il sistema abbia un mucchio di richieste di lettura I/O quasi contemporaneamente, il che può far sì che il sistema operativo legga piccoli pezzi di ciascuno a sua volta - causando la testa della testa di salto avanti e indietro, sprecando preziosi millisecondi.

Ma è solo un'ipotesi; Non è possibile determinare davvero cosa sta causando il tuo rallentamento senza saperne di più.

Sebbene la creazione di thread sia "estremamente costosa" rispetto a dire che aggiunge due numeri, di solito non è qualcosa che esagerai facilmente. Se le tue operazioni sono estremamente brevi (diciamo, un millisecondo o meno), l'uso di un pool di thread anziché nuovi thread risparmierà notevolmente il tempo. In generale, se le tue operazioni sono così brevi, dovresti comunque riconsiderare la granularità del parallelismo; Forse stai meglio dividendo il calcolo in blocchi più grandi: ad esempio, avendo un numero abbastanza basso di compiti dei lavoratori che gestiscono interi lotti di elementi di lavoro più piccoli alla volta anziché ogni articolo separatamente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top