Il seguente test nunit confronta prestazioni tra l'esecuzione di un singolo filo rispetto esecuzione 2 fili su una macchina dual core. In particolare, si tratta di una macchina virtuale Windows 7 dual core VMWare su un host SLED Linux quad core con un Dell Inspiron 503.Prestazioni dual-core peggiori rispetto al single core?
Ogni thread scorre semplicemente e incrementa 2 contatori, addCounter e readCounter. Questo test è stato inizialmente testato su un'implementazione di Queue, che è stata scoperta peggiorare su una macchina multi-core. Quindi nel restringere il problema al piccolo codice riproducibile, hai qui nessuna coda solo incrementando le variabili e per scioccare e sgomentare, è molto più lento con 2 thread e uno.
Quando si esegue il primo test, il Task Manager mostra 1 dei core occupati al 100% con l'altro core quasi inattivo. Ecco l'output di test per il test a singolo thread:
readCounter 360687000
readCounter2 0
total readCounter 360687000
addCounter 360687000
addCounter2 0
Si vedono oltre 360 milioni di incrementi!
Successivamente il test a doppio filo mostra il 100% occupato su entrambi i core per l'intera durata di 5 secondi del test. Tuttavia, viene mostrato solo l'output:
readCounter 88687000
readCounter2 134606500
totoal readCounter 223293500
addCounter 88687000
addCounter2 67303250
addFailure0
Questo è solo 223 milioni di incrementi di lettura. Qual è la creazione di Dio sono quei 2 CPU che fanno per quei 5 secondi per ottenere meno lavoro svolto?
Eventuali indizi? E puoi eseguire i test sulla tua macchina per vedere se ottieni risultati diversi? Un'idea è che forse la prestazione dual core VMWare non è ciò che si spera.
Modifica: ho provato quanto sopra su un laptop quad core senza vmware e ho ottenuto prestazioni degradate simili. Così ho scritto un altro test simile al precedente ma che ha ciascun metodo di thread in una classe separata. Il mio scopo era quello di testare 4 core.
Bene, questo test ha mostrato risultati eccellenti che sono migliorati quasi linearmente con 1, 2, 3 o 4 core.
Con alcuni esperimenti ora su entrambe le macchine, sembra che le prestazioni appropriate avvengano solo se i metodi di thread principale sono su istanze diverse invece che sulla stessa istanza.
In altre parole, se sulla stessa istanza di una particolare classe si utilizza il metodo di immissione principale di più thread, le prestazioni su un multi-core peggioreranno per ogni thread che si aggiunge, anziché come meglio si potrebbe assumere.
Sembra quasi che il CLR si "sincronizzi" in modo che solo un thread alla volta possa essere eseguito su tale metodo. Tuttavia, i miei test dicono che non è il caso. Quindi non è ancora chiaro cosa sta succedendo.
Ma il mio problema sembra essere risolto semplicemente rendendo istanze separate di metodi per eseguire thread come punto di partenza.
saluti, Wayne
EDIT:
Ecco un test di unità aggiornato che test 1, 2, 3, 4 & filettature con tutti sulla stessa istanza di una classe. L'utilizzo di matrici con variabili utilizza nel ciclo del filo almeno 10 elementi separati. E le prestazioni si degradano ancora significativamente per ogni thread aggiunto.
using System;
using System.Threading;
using NUnit.Framework;
namespace TickZoom.Utilities.TickZoom.Utilities
{
[TestFixture]
public class MultiCoreSameClassTest
{
private ThreadTester threadTester;
public class ThreadTester
{
private Thread[] speedThread = new Thread[400];
private long[] addCounter = new long[400];
private long[] readCounter = new long[400];
private bool[] stopThread = new bool[400];
internal Exception threadException;
private int count;
public ThreadTester(int count)
{
for(var i=0; i<speedThread.Length; i+=10)
{
speedThread[i] = new Thread(SpeedTestLoop);
}
this.count = count;
}
public void Run()
{
for (var i = 0; i < count*10; i+=10)
{
speedThread[i].Start(i);
}
}
public void Stop()
{
for (var i = 0; i < stopThread.Length; i+=10)
{
stopThread[i] = true;
}
for (var i = 0; i < count * 10; i += 10)
{
speedThread[i].Join();
}
if (threadException != null)
{
throw new Exception("Thread failed: ", threadException);
}
}
public void Output()
{
var readSum = 0L;
var addSum = 0L;
for (var i = 0; i < count; i++)
{
readSum += readCounter[i];
addSum += addCounter[i];
}
Console.Out.WriteLine("Thread readCounter " + readSum + ", addCounter " + addSum);
}
private void SpeedTestLoop(object indexarg)
{
var index = (int) indexarg;
try
{
while (!stopThread[index*10])
{
for (var i = 0; i < 500; i++)
{
++addCounter[index*10];
}
for (var i = 0; i < 500; i++)
{
++readCounter[index*10];
}
}
}
catch (Exception ex)
{
threadException = ex;
}
}
}
[SetUp]
public void Setup()
{
}
[Test]
public void SingleCoreTest()
{
TestCores(1);
}
[Test]
public void DualCoreTest()
{
TestCores(2);
}
[Test]
public void TriCoreTest()
{
TestCores(3);
}
[Test]
public void QuadCoreTest()
{
TestCores(4);
}
public void TestCores(int numCores)
{
threadTester = new ThreadTester(numCores);
threadTester.Run();
Thread.Sleep(5000);
threadTester.Stop();
threadTester.Output();
}
}
}
Si sta eseguendo questo in modalità di rilascio senza il debugger allegato? –
Nota: il codice non ha operazioni di sincronizzazione thread (blocchi o interbloccati o altro). Se la si mantiene così, si può anche usare Random per tutti i valori poiché non c'è modo di avere il codice multithread in esecuzione corretta senza sincronizzazione. –
Jim, ci ho provato in entrambi i modi. Ma i numeri discussi sono durante la modalità Debug. Alexel ... sono consapevole della necessità di serrature e interfogliati. Ovviamente. Ma questo è solo un codice sperimentale. L'esperimento è iniziato con le serrature ma le prestazioni erano orribili, peggio con più thread/core. Così ho rimosso i lucchetti per vedere se è più veloce. No ... ancora male. Quindi sto cercando di isolare perché 4 core eseguono lo stesso codice senza alcun blocco quindi cane lento ??? Sai perché? – Wayne