2009-02-10 6 views
5
#include <iostream> 
#include <vector> 
using namespace std; 

int main() 
{ 
    vector< vector<int> > dp(50000, vector<int>(4, -1)); 
    cout << dp.size(); 
} 

Questo piccolo programma impiega una frazione di secondo per essere eseguito quando viene eseguito semplicemente dalla riga di comando. Ma quando viene eseguito in un debugger, ci vogliono più di 8 secondi. La sospensione del debugger rivela che è nel mezzo di distruggere tutti quei vettori. WTF?Comportamento strano dei distruttori C++

Nota: Visual Studio 2008 SP1, CPU Core 2 Duo 6700 con 2 GB di RAM.

Aggiunto: Per chiarire, no, non sto confondendo le versioni di debug e release. Questi risultati sono su uno stesso file .exe, senza alcuna ricompilazione tra di essi. In effetti, il passaggio tra le build di Debug e Release non cambia nulla.

+0

Sei sicuro che non stai confrontando il debug vs release si basa? –

risposta

18

L'esecuzione nel debugger modifica la libreria di allocazione di memoria utilizzata a una che esegue molti più controlli. Un programma che non fa altro che allocazione e de-allocazione della memoria soffrirà molto più di un programma "normale".

Modifica Avendo appena provato a fare funzionare il vostro programma sotto VS ricevo uno stack di chiamate che assomiglia

[email protected]() + 0x117 bytes  
[email protected]() + 0x97 bytes 
[email protected]() + 0x228bf bytes 
[email protected]() + 0x17646 bytes  
msvcr90d.dll!_free_base(void * pBlock=0x0061f6e8) Line 109 + 0x13 bytes 
msvcr90d.dll!_free_dbg_nolock(void * pUserData=0x0061f708, int nBlockUse=1) 
msvcr90d.dll!_free_dbg(void * pUserData=0x0061f708, int nBlockUse=1) 
msvcr90d.dll!operator delete(void * pUserData=0x0061f708) 
desc.exe!std::allocator<int>::deallocate(int * _Ptr=0x0061f708, unsigned int __formal=4) 
desc.exe!std::vector<int,std::allocator<int> >::_Tidy() Line 1134 C++ 

che mostra le funzioni di debug in ntdll.dll e il runtime C in uso.

+0

Ma non ricompongo il programma tra parentesi! È lo stesso .EXE! –

+1

Non è necessario ricompilare il programma se l'allocazione della memoria è in una DLL o .so. –

+1

Come dice Paul l'allocazione di memoria nella a dll, quindi la ricompilazione o meno non ha importanza (a meno che tu non abbia collegato staticamente tutto - anche allora potrebbe usare la chiamata IsDebuggerPresent se hai costruito contro le librerie di debug, io no so che non ho mai avuto bisogno di andare così in profondità). –

3

L'esecuzione di un programma con il debugger collegato è sempre più lento che senza.

Questo deve essere causato da VS che si collega alle nuove/cancellazioni di chiamate e fa più controllo quando è collegato - oppure la libreria di runtime utilizza l'API IsDebuggerPresent e fa cose diverse in quel caso.

Si può facilmente provare questo da dentro Visual Studio, avviare il programma con Debug-> Avvia Debug o Debug-> Avvia senza debug. Senza il debug è come da riga di comando, con esattamente la stessa configurazione di build ed eseguibile.

+0

Nessun dubbio. Ma non ho mai visto un rallentamento di questa grandezza! –

+0

No, in realtà questo non è ciò che sta causando questo. L'esecuzione di un programma con un debugger collegato non lo rende necessariamente più lento. Un debugger è semplicemente un processo in attesa che un altro processo generi un'eccezione o colpisca un breakpoint. –

+0

Prova tu stesso il programma di esempio, avvialo con F5 o Ctrl + F5. È incredibilmente veloce senza il debugger collegato ... – Timbo

0

Sì, WTF davvero.

Sapete che il vostro compilatore ottimizzerà molte di queste chiamate di funzioni inserendole e quindi ottimizzerà ulteriormente il codice lì per escludere tutto ciò che in realtà non sta facendo nulla, che nel caso di vettori di int significa: grazioso molto non molto

In modalità di debug, l'inlining non è attivato perché ciò renderebbe il debug terribile.

Questo è un bell'esempio di quanto sia veloce il codice C++.

+0

Ti manca il punto. Lo stesso exe, senza ricompilare, ha la differenza di velocità. –

+0

Ok, bene, allora il mio anser ha torto. –

-1

non ha senso per me: collegare un debugger a un binario casuale in una configurazione normale dovrebbe principalmente interrompere gli interrupt di breakpoint (asm int 3, ecc.).

+0

Compilarlo e vedere di persona! :) –

+0

Oppure scambia nelle librerie per raccogliere più informazioni sul debugger. –

2

È decisamente HeapFree che sta rallentando, è possibile ottenere lo stesso effetto con il programma seguente.

Il passaggio di parametri come HEAP_NO_SERIALIZE a HeapFree non aiuta neanche.

#include "stdafx.h" 
#include <iostream> 
#include <windows.h> 

using namespace std; 


int _tmain(int argc, _TCHAR* argv[]) 
{ 
HANDLE heap = HeapCreate(0, 0, 0); 

void** pointers = new void*[50000]; 

int i = 0; 
for (i = 0; i < 50000; ++i) 
{ 
    pointers[i] = HeapAlloc(heap, 0, 4 * sizeof(int)); 
} 

cout << i; 
for (i = 49999; i >= 0; --i) 
{ 
    HeapFree(heap, 0, pointers[i]); 
} 

cout << "!"; 

delete [] pointers; 

HeapDestroy(heap); 
} 
0

8 secondi ?? Ho provato lo stesso in modalità debug. Non più di mezzo secondo, credo. Sei sicuro che siano i distruttori?

FYI. Visual Studio 2008 SP1, CPU Core 2 Duo 6700 con 2 GB di RAM.

3

L'heap di debug viene abilitato automaticamente quando si avvia il programma nel debugger, invece di collegarsi a un programma già in esecuzione con il debugger.

Il libro Advanced Windows Debugging da Mario Hewardt e Daniel Pravat ha alcune informazioni decente sul mucchio di Windows, e si scopre che il capitolo sui cumuli è up on the web site as a sample chapter.

Pagina 281 ha una barra laterale su "Collegamento Versus Avvio del processo nel debugger":

Quando si avvia il processo nel debugger , il gestore di heap modifica tutte le richieste per creare nuovi cumuli e cambiamento i flag di creazione dell'heap su abilitano gli heap debug-friendly (a meno che lo la variabile di ambiente _NO_DEBUG_HEAP sia impostato su 1). In confronto, allegando a un processo già in esecuzione, i mucchi nel processo di avere già stata creata usando predefinita bandiere creazione mucchio e non avranno i flag di debug ambientale, situato (a meno che non esplicitamente impostato dall'applicazione).

(anche:. a semi-related question, dove ho postato parte di questa risposta prima)

1

http://www.symantec.com/connect/articles/windows-anti-debug-reference

sezioni Leggi 2 "! PEB NtGlobalFlags" e 2 "bandiere Heap"

che questo può spiegarlo ...


MODIFICA: aggiunta soluzione

nel vostro gestore per CREATE_PROCESS_DEBUG_EVENT, aggiungere il seguente

// hack 'Load Configuration Directory' in exe header to point to a new block that specfies GlobalFlags 
IMAGE_DOS_HEADER dos_header; 
ReadProcessMemory(cpdi.hProcess,cpdi.lpBaseOfImage,&dos_header,sizeof(IMAGE_DOS_HEADER),NULL); 
IMAGE_OPTIONAL_HEADER32 pe_header; 
ReadProcessMemory(cpdi.hProcess,(BYTE*)cpdi.lpBaseOfImage+dos_header.e_lfanew+4+sizeof(IMAGE_FILE_HEADER),&pe_header,offsetof(IMAGE_OPTIONAL_HEADER32,DataDirectory),NULL); 
IMAGE_LOAD_CONFIG_DIRECTORY32 ilcd; 
ZeroMemory(&ilcd,sizeof(ilcd)); 
ilcd.Size = 64; // not sizeof(ilcd), as 2000/XP didn't have SEHandler 
ilcd.GlobalFlagsClear = 0xffffffff; // clear all flags. this is as we don't want dbg heap 
BYTE *p = (BYTE *)VirtualAllocEx(cpdi.hProcess,NULL,ilcd.Size,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE); 
WriteProcessMemory(cpdi.hProcess,p,&ilcd,ilcd.Size,NULL); 
BYTE *dde = (BYTE*)cpdi.lpBaseOfImage+dos_header.e_lfanew+4+sizeof(IMAGE_FILE_HEADER)+offsetof(IMAGE_OPTIONAL_HEADER32,DataDirectory)+sizeof(IMAGE_DATA_DIRECTORY)*IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG; 
IMAGE_DATA_DIRECTORY temp; 
temp.VirtualAddress = p-cpdi.lpBaseOfImage; 
temp.Size = ilcd.Size; 
DWORD oldprotect; 
VirtualProtectEx(cpdi.hProcess,dde,sizeof(temp),PAGE_READWRITE,&oldprotect); 
WriteProcessMemory(cpdi.hProcess,dde,&temp,sizeof(temp),NULL); 
VirtualProtectEx(cpdi.hProcess,dde,sizeof(temp),oldprotect,&oldprotect);