Nous avons mis en place deux identiques HP Z840 des Postes de travail avec les spécifications suivantes
- 2 x Xeon E5-2690 v4 @ 2.60 GHz (Turbo Boost SUR, HT OFF, total 28 Processeurs logiques)
- 32GB DDR4 2400 Mémoire, Quad-channel
et installé Windows 7 SP1 (x64), Windows 10 Créateurs de mise à Jour (x64) sur chaque.
Ensuite, nous avons fait un petit mémoire de référence (code ci-dessous, construit avec VS2015 mise à Jour 3, l'architecture 64 bits) qui effectue l'allocation de la mémoire-remplissage-gratuit simultanément à partir de plusieurs threads.
#include <Windows.h>
#include <vector>
#include <ppl.h>
unsigned __int64 ZQueryPerformanceCounter()
{
unsigned __int64 c;
::QueryPerformanceCounter((LARGE_INTEGER *)&c);
return c;
}
unsigned __int64 ZQueryPerformanceFrequency()
{
unsigned __int64 c;
::QueryPerformanceFrequency((LARGE_INTEGER *)&c);
return c;
}
class CZPerfCounter {
public:
CZPerfCounter() : m_st(ZQueryPerformanceCounter()) {};
void reset() { m_st = ZQueryPerformanceCounter(); };
unsigned __int64 elapsedCount() { return ZQueryPerformanceCounter() - m_st; };
unsigned long elapsedMS() { return (unsigned long)(elapsedCount() * 1000 / m_freq); };
unsigned long elapsedMicroSec() { return (unsigned long)(elapsedCount() * 1000 * 1000 / m_freq); };
static unsigned __int64 frequency() { return m_freq; };
private:
unsigned __int64 m_st;
static unsigned __int64 m_freq;
};
unsigned __int64 CZPerfCounter::m_freq = ZQueryPerformanceFrequency();
int main(int argc, char ** argv)
{
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
int ncpu = sysinfo.dwNumberOfProcessors;
if (argc == 2) {
ncpu = atoi(argv[1]);
}
{
printf("No of threads %d\n", ncpu);
try {
concurrency::Scheduler::ResetDefaultSchedulerPolicy();
int min_threads = 1;
int max_threads = ncpu;
concurrency::SchedulerPolicy policy
(2 // two entries of policy settings
, concurrency::MinConcurrency, min_threads
, concurrency::MaxConcurrency, max_threads
);
concurrency::Scheduler::SetDefaultSchedulerPolicy(policy);
}
catch (concurrency::default_scheduler_exists &) {
printf("Cannot set concurrency runtime scheduler policy (Default scheduler already exists).\n");
}
static int cnt = 100;
static int num_fills = 1;
CZPerfCounter pcTotal;
// malloc/free
printf("malloc/free\n");
{
CZPerfCounter pc;
for (int i = 1 * 1024 * 1024; i <= 8 * 1024 * 1024; i *= 2) {
concurrency::parallel_for(0, 50, [i](size_t x) {
std::vector<void *> ptrs;
ptrs.reserve(cnt);
for (int n = 0; n < cnt; n++) {
auto p = malloc(i);
ptrs.emplace_back(p);
}
for (int x = 0; x < num_fills; x++) {
for (auto p : ptrs) {
memset(p, num_fills, i);
}
}
for (auto p : ptrs) {
free(p);
}
});
printf("size %4d MB, elapsed %8.2f s, \n", i / (1024 * 1024), pc.elapsedMS() / 1000.0);
pc.reset();
}
}
printf("\n");
printf("Total %6.2f s\n", pcTotal.elapsedMS() / 1000.0);
}
return 0;
}
Étonnamment, le résultat est très mauvais dans Windows 10 CU par rapport à Windows 7. J'ai tracé le résultat ci-dessous pour la taille de bloc de 1 mo et 8 mo de taille de bloc, en variant le nombre de threads de 2,4,.., jusqu'à 28. Alors que Windows 7 a donné un peu moins bonne performance lorsque nous avons augmenté le nombre de threads, Windows 10 a donné bien pire évolutivité.
Nous avons essayé de assurez-vous que toutes les Fenêtres de mise à jour est appliquée, la mise à jour de pilotes, d'ajuster les paramètres du BIOS, sans succès. Nous avons également effectué le même test sur plusieurs autres plates-formes matérielles, et toutes ont donné courbe similaire pour Windows 10. Il semble donc y avoir un problème de Windows 10.
Quelqu'un aurait-il une expérience similaire, ou peut-être de savoir-faire sur ce sujet (peut-être que nous avons raté quelque chose ?). Ce comportement a fait de notre application multithread ai significative des performances.
*** ÉDITÉ
À l'aide de https://github.com/google/UIforETW (merci à Bruce Dawson) pour analyser l'indice de référence, nous avons constaté que la plupart du temps est passé à l'intérieur du grain KiPageFault. Creuser plus loin en bas de l'arbre d'appel, conduit à tous les ExpWaitForSpinLockExclusiveAndAcquire. Semble que le verrouillage est à l'origine de ce problème.
*** ÉDITÉ
Recueillies Server 2012 R2 de données sur le même matériel. Server 2012 R2 est aussi pire que Win7, mais toujours beaucoup mieux que Win10 CU.
*** ÉDITÉ
Il arrive dans le Serveur de 2016 ainsi. J'ai ajouté la balise windows-server-2016.
*** ÉDITÉ
À l'aide de info de @Ext3h, j'ai modifié l'indice de référence à utiliser VirtualAlloc et VirtualLock. Je peux confirmé une amélioration significative par rapport à quand VirtualLock n'est pas utilisé. Ensemble Win10 est toujours de 30% à 40% plus lent que Win7 lorsque les deux à l'aide de VirtualAlloc et VirtualLock.