Problèmes de désallocation de mémoire du compilateur VS2012

J’ai un problème étrange avec le compilateur VS2012 qui ne semble pas apparaître dans GCC. Le processus de désallocation finit par prendre quelques minutes plutôt que des secondes. Quelqu’un at-il des commentaires à ce sujet? Le débogage par étapes montre un blocage notable des appels à RtlpCollectFreeBlocks (). J’ai ce problème dans le mode de débogage et de libération. J’utilise Windows 7 32 bits, mais j’ai le même problème avec le 64 bits 7.

#include "stdafx.h" #include  #include  #include  #define SIZE 500000 using namespace std; typedef struct { uint32_t* thing1; }collection; /* * VS2012 comstackr used. * Scenarios: * 1) Don't allocate thing1. Program runs poorly. * 2) Allocate thing1 but don't delete it. Program runs awesome. * 3) Allocate thing1 and delete it. Program runs poorly. * * Debug or Release mode does not affect outcome. GCC's comstackr is fine. */ int _tmain(int argc, _TCHAR* argv[]) { collection ** colArray = new collection*[SIZE]; for(int i=0;ithing1 = new uint32_t; // Allocating without freeing runs fine. Either A) don't allocate or B) allocate and delete to make it run slow. colArray[i] = mine; } cout<<"Done with assignment\n"; for(int i=0;ithing1); // delete makes it run poorly. delete(colArray[i]); if(i > 0 && i%100000 == 0) { cout<<"100 thousand deleted\n"; } } delete [] colArray; cout <>x; } 

Le problème de performances que vous rencontrez provient de la fonctionnalité du tas de débogage Windows, et il est un peu furtif dans sa manière de s’activer, même dans les versions de publication.

J’ai pris la liberté de créer une image de débogage 64 bits d’un programme plus simple et j’ai découvert ceci:

  • msvcr110d.dll! _CrtIsValidHeapPointer (const void * pUserData = 0x0000000001a8b540)
  • msvcr110d.dll! _free_dbg_nolock (void * pUserData = 0x0000000001a8b540, int nBlockUse = 1)
  • msvcr110d.dll! _free_dbg (void * pUserData = 0x0000000001a8b540, int nBlockUse = 1)
  • msvcr110d.dll! operator delete (void * pUserData = 0x0000000001a8b540)

Le corps de msvcr110d.dll!_CrtIsValidHeapPointer qui se révèle être le suivant:

 if (!pUserData) return FALSE; // Note: all this does is checks for null if (!_CrtIsValidPointer(pHdr(pUserData), sizeof(_CrtMemBlockHeader), FALSE)) return FALSE; // but this is expensive return HeapValidate( _crtheap, 0, pHdr(pUserData) ); 

Cet appel HeapValidate() est brutal.

Ok, peut-être je m’attendrais à cela dans une version de débogage. mais certainement pas libérer. En fin de compte, cela s’améliore, mais regardez la stack d’appels:

  • ntdll.dll! RtlDebugFreeHeap ()
  • ntdll.dll! ssortingng “Activation des options de débogage du tas \ n” ()
  • ntdll.dll! RtlFreeHeap ()
  • kernel32.dll! HeapFree ()
  • msvcr110.dll! free (void * pBlock)

Ceci est intéressant, car lorsque je l’ai exécuté en premier, puis que je l’ai attaché au processus en cours avec l’IDE (ou WinDbg) sans lui permettre de contrôler l’environnement de démarrage, cette stack s’arrête à ntdll.dll!RtlFreeHeap() . En d’autres termes, l’exécution en dehors de l’IDE RtlDebugFreeHeap n’est pas appelée. Mais pourquoi??

Je me suis dit que le débogueur retournait en quelque sorte un commutateur pour activer le débogage du tas. Après quelques recherches, j’ai découvert que “switch” était le débogueur lui-même. Windows utilise les fonctions de RtlDebugAllocHeap débogage spéciales ( RtlDebugAllocHeap et RtlDebugFreeHeap ) si le processus en cours d’exécution est généré par un débogueur. Cette page de manuel de MSDN sur WinDbg élude cela, ainsi que d’autres informations intéressantes sur le débogage sous Windows:

du débogage d’un processus en mode utilisateur à l’aide de WinDbg

Les processus créés par le débogueur (également appelés processus générés) se comportent légèrement différemment des processus que le débogueur ne crée pas.

Au lieu d’utiliser l’API de segment de mémoire standard, les processus créés par le débogueur utilisent un segment de débogage spécial. Vous pouvez forcer un processus généré à utiliser le segment de mémoire standard au lieu du segment de débogage à l’aide de la variable d’environnement _NO_DEBUG_HEAP ou de l’option de ligne de commande -hd.

Maintenant, nous allons quelque part. Pour tester cela, j’ai simplement laissé un sleep() avec un temps suffisant pour que je puisse attacher le débogueur plutôt que de générer le processus avec lui, puis le laisser s’exécuter de manière joyeuse. Bien sûr, comme mentionné précédemment, il a navigué à toute vitesse.

Basé sur le contenu de cet article, j’ai pris la liberté de mettre à jour mes versions en mode Release pour définir _NO_DEBUG_HEAP=1 dans leurs parameters d’environnement d’exécution de mes fichiers de projet. Je suis évidemment toujours intéressé par l’activité de tas granulaire dans les versions de débogage, donc ces configurations sont restées telles quelles. Après cela, la vitesse globale de mes versions exécutées sous VS2012 (et VS2010) était sensiblement plus rapide et je vous invite à essayer également.