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

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

Chương trình nhỏ này mất một giây để thực thi khi chỉ cần chạy từ dòng lệnh. Nhưng khi chạy trong trình gỡ rối, phải mất hơn 8 giây. Tạm dừng trình gỡ lỗi cho thấy rằng nó đang ở giữa phá hủy tất cả các vectơ đó. WTF?Hành vi lạ của C++ destructors

Lưu ý - Visual Studio 2008 SP1, CPU Core 2 Duo 6700 có RAM 2GB.

Đã thêm: Để làm rõ, không, tôi không nhầm lẫn các bản dựng Gỡ lỗi và Bản phát hành. Những kết quả này trên một và cùng .exe, thậm chí không có bất kỳ sự biên dịch lại nào. Trong thực tế, việc chuyển đổi giữa Debug và Release builds không thay đổi gì cả.

+0

Bạn có chắc chắn bạn không so sánh debug vs phát hành xây dựng? –

Trả lời

18

Chạy trong trình gỡ lỗi thay đổi thư viện cấp phát bộ nhớ được sử dụng cho thư viện thực hiện kiểm tra nhiều hơn. Một chương trình không làm gì ngoài việc cấp phát bộ nhớ và phân bổ de sẽ bị ảnh hưởng nhiều hơn chương trình "bình thường".

Sửa Sau khi chỉ cố gắng chạy chương trình của bạn dưới VS tôi nhận được một cuộc gọi stack trông giống như

[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++ 

nào cho thấy các chức năng debug trong ntdll.dll và thời gian chạy C được sử dụng.

+0

Nhưng tôi không biên dịch lại chương trình inbetween! Nó giống nhau. EXE! –

+1

Bạn không phải biên dịch lại chương trình nếu cấp phát bộ nhớ trong một tệp DLL hoặc .so. –

+1

Như Paul nói phân bổ bộ nhớ trong dll, do đó biên dịch lại hay không không quan trọng (trừ khi bạn đã liên kết tĩnh mọi thứ - thậm chí sau đó nó có thể sử dụng cuộc gọi IsDebuggerPresent nếu bạn đã xây dựng chống lại các thư viện gỡ lỗi, tôi không biết tôi chưa bao giờ cần phải đi sâu đến thế). –

3

Chạy chương trình có trình gỡ lỗi được đính kèm luôn chậm hơn không.

Điều này phải do VS hooking vào cuộc gọi mới/xóa và thực hiện kiểm tra nhiều hơn khi được đính kèm - hoặc thư viện thời gian chạy sử dụng API IsDebuggerPresent và thực hiện những việc khác trong trường hợp đó.

Bạn có thể dễ dàng thử điều này từ bên trong Visual Studio, khởi động chương trình bằng Debug-> Bắt đầu gỡ lỗi hoặc Debug-> Bắt đầu không gỡ lỗi. Nếu không có gỡ lỗi giống như từ dòng lệnh, với chính xác cùng một cấu hình xây dựng và thực thi.

+0

Không nghi ngờ gì. Nhưng tôi chưa bao giờ thấy sự suy giảm của cường độ này! –

+0

Không, thực sự đây không phải là nguyên nhân gây ra điều này. Việc chạy chương trình có trình gỡ rối được đính kèm không nhất thiết phải làm chậm hơn. Trình gỡ rối chỉ đơn giản là một quá trình chờ đợi một quá trình khác để ném một ngoại lệ hoặc nhấn một điểm ngắt. –

+0

Tự mình thử chương trình mẫu, Bắt đầu bằng F5 hoặc Ctrl + F5. Đó là nhanh chóng mà không cần debugger đính kèm ... – Timbo

0

Vâng, WTF thực sự.

Bạn biết trình biên dịch của bạn sẽ tối ưu hóa rất nhiều chức năng gọi bằng cách gạch chân chúng, và sau đó tối ưu hóa mã ở đó để loại trừ bất kỳ thứ gì không thực sự làm bất cứ điều gì, trong trường hợp vectơ của int sẽ có nghĩa là: không nhiều lắm.

Trong chế độ gỡ lỗi, nội tuyến không được bật vì điều đó sẽ làm cho việc gỡ rối khủng khiếp.

Đây là ví dụ điển hình về việc mã C++ có thể thực sự nhanh như thế nào.

+0

Bạn bỏ lỡ vấn đề. Cùng .exe, không biên dịch lại, có sự khác biệt về tốc độ. –

+0

Được rồi, sau đó anser của tôi là sai. –

-1

không có ý nghĩa gì đối với tôi - việc đính kèm trình gỡ rối vào nhị phân ngẫu nhiên trong cấu hình bình thường chủ yếu chỉ là ngắt ngắt điểm ngắt (asm int 3, v.v.).

+0

Biên dịch và tự mình xem! :) –

+0

Hoặc trao đổi trong thư viện để thu thập thêm thông tin về trình gỡ lỗi. –

2

Chắc chắn HeapFree đang làm chậm việc này xuống, bạn có thể có hiệu ứng tương tự với chương trình bên dưới.

Việc truyền tham số như HEAP_NO_SERIALIZE cho HeapFree cũng không giúp ích gì.

#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 giây ?? Tôi đã thử trong chế độ Debug. Không quá nửa giây tôi đoán vậy. Bạn có chắc chắn đó là destructors?

FYI. Visual Studio 2008 SP1, CPU Core 2 Duo 6700 với 2GB RAM.

3

Heap gỡ lỗi tự động được bật khi bạn khởi động chương trình của bạn trong trình gỡ lỗi, thay vì đính kèm vào chương trình đã chạy với trình gỡ lỗi.

Cuốn sách Advanced Windows Debugging bởi Mario Hewardt và Daniel Pravat có một số thông tin khá về đống Windows, và nó chỉ ra rằng chương về đống là up on the web site as a sample chapter.

trang 281 có một thanh bên về "Gắn Versus Bắt đầu quá trình Theo Debugger":

Khi bắt đầu quá trình dưới debugger , người quản lý đống đổi mọi yêu cầu để tạo ra đống mới và thay đổi cờ tạo heap để bật heap gỡ lỗi thân thiện (trừ khi môi trường _NO_DEBUG_HEAP được đặt thành 1). Trong khi đó, gắn với một quá trình đã chạy, các đống trong quá trình này có đã được tạo ra sử dụng mặc định cờ tạo đống và sẽ không có cờ debug thân thiện set (trừ khi một cách rõ ràng theo quy định của ứng dụng).

(Ngoài ra:. a semi-related question, nơi tôi đăng tải một phần của câu trả lời này trước)

1

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

phần đọc 2 "! NtGlobalFlags PEB" và 2 "cờ Heap"

nghĩ rằng đây có thể giải thích ...


EDIT: giải pháp được thêm

trong xử lý của bạn cho CREATE_PROCESS_DEBUG_EVENT, thêm dòng sau

// 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);