使用CreateProcess时速度变慢

问题描述 投票:1回答:1

让我从一些示例代码开始。我为此做了一个最小的测试用例。要复制,需要两块:

第一个可执行文件,一个使用CreateProcess的小型应用程序。我们称它为[[Debugger。

#include <Windows.h> #include <string> #include <iostream> #include <vector> int main() { STARTUPINFO si = {0}; PROCESS_INFORMATION pi = {0}; si.cb = sizeof(si); // Starts the 'App': auto exe = L"C:\\Tests\\x64\\Release\\TestProject.exe"; std::vector<wchar_t> tmp; tmp.resize(1024); memcpy(tmp.data(), exe, (1 + wcslen(exe)) * sizeof(wchar_t)); auto result = CreateProcess(NULL, tmp.data(), NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); DEBUG_EVENT debugEvent = { 0 }; bool continueDebugging = true; while (continueDebugging) { if (WaitForDebugEvent(&debugEvent, INFINITE)) { std::cout << "Event " << debugEvent.dwDebugEventCode << std::endl; if (debugEvent.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) { continueDebugging = false; } // I real life, this is more complicated... For a minimum test, this will do auto continueStatus = DBG_CONTINUE; ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, continueStatus); } } std::cout << "Done." << std::endl; std::string s; std::getline(std::cin, s); return 0; }
第二个可执行文件,一个小的应用程序,它做一些愚蠢的事情会消耗​​时间。我们称其为

App

#include <Windows.h> #include <iostream> #include <string> #include <vector> __declspec(noinline) void CopyVector(uint64_t value, std::vector<uint8_t> data) { // irrelevant. data.resize(10); *reinterpret_cast<uint64_t*>(data.data()) = value; } int main(int argc, const char** argv) { for (int i = 0; i < 10; ++i) { LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds; LARGE_INTEGER Frequency; QueryPerformanceFrequency(&Frequency); QueryPerformanceCounter(&StartingTime); // Activity to be timed std::vector<uint8_t> tmp; tmp.reserve(10'000'000 * 8); // The activity (*) uint64_t v = argc; for (size_t j = 0; j < 10'000'000; ++j) { v = v * 78239742 + 1278321; CopyVector(v, tmp); } QueryPerformanceCounter(&EndingTime); ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart; // We now have the elapsed number of ticks, along with the // number of ticks-per-second. We use these values // to convert to the number of elapsed microseconds. // To guard against loss-of-precision, we convert // to microseconds *before* dividing by ticks-per-second. ElapsedMicroseconds.QuadPart *= 1000000; ElapsedMicroseconds.QuadPart /= Frequency.QuadPart; std::cout << "Elapsed: " << ElapsedMicroseconds.QuadPart << " microsecs" << std::endl; } std::string s; std::getline(std::cin, s); }
请注意

debugger

应用程序实际上并未执行任何操作。它只是坐在那里,等待app完成。我正在使用最新版本的VS2019。现在已经测试了四种方案。对于每种情况,我都为单次迭代(变量i)花费了时间。我期望的是,运行

App

(1)和运行Debugger(4)的速度大约相同(因为Debugger并没有做任何事情)。但是,现实却大不相同:
    运行
  1. App
(Windows资源管理器/ Ctrl-F5)。在我的电脑上,每次迭代大约需要1秒。在Visual Studio调试器(F5)中<App。同样,每次迭代大约1秒。我会期望的。在Visual Studio调试器(F5)中<Debugger。同样,每次迭代大约1秒。同样,我期望的是。运行
  • Debugger
  • (仅从Windows资源管理器或ctrl-F5)。这次,我们必须等待大约。每次迭代4秒(!)。不是我所期望的!我将问题缩小为vector<uint8_t> data参数,该参数通过值传递(称为副本c'tor)。
  • 我非常想知道这里发生的事情...为什么运行[[debugger
  • 的速度慢4倍,而它却什么也没做?-更新-

    我已经使用专有库向我的小调试器程序中添加了一些堆栈跟踪和性能分析功能...以将情况(3)和(4)相互比较。我基本上已经计算出堆栈跟踪中指针的出现频率。

    这些方法可以在情况(4)的结果中找到,但在情况(3)中不重要。开头的数字是一个简单的计数器:

    352 - inside memset (address: 0x7ffa727349d5) 284 - inside RtlpNtMakeTemporaryKey (address: 0x7ffa727848b2) 283 - inside RtlAllocateHeap (address: 0x7ffa726bbaba) 261 - inside memset (address: 0x7ffa727356af) 180 - inside RtlFreeHeap (address: 0x7ffa726bfc10) 167 - inside RtlpNtMakeTemporaryKey (address: 0x7ffa72785408) 161 - inside RtlGetCurrentServiceSessionId (address: 0x7ffa726c080f)

    特别是RtlpNtMakeTemporaryKey似乎出现了很多。不幸的是,我不知道这意味着什么,而且Google似乎没有帮助...

    c++ winapi visual-c++ createprocess
    1个回答
    0
    投票
    NtGlobalFlag(在PEB中)以调试堆使用(FLG_HEAP_ENABLE_TAIL_CHECKFLG_HEAP_ENABLE_FREE_CHECKFLG_HEAP_VALIDATE_PARAMETERS)所有这些检查并用特殊模式(baadf00dabababab填充所有分配的块]在块末尾)使所有堆分配/空闲变慢(在没有这种情况下进行比较)

    从程序的另一面来看,大多数时间用于从堆分配/释放内存。

    配置文件也显示此-RtlAllocateHeapmemset-确定分配的块中是否填充了魔术图案,RtlpNtMakeTemporaryKey-此由单个指令组成的“功能”-jmp ZwDeleteKey-因此,您确实不在此功能内,但在靠近堆的另一个函数中,“靠近”它。

    如所述

    Simon Mourier

    -为什么情况(2)和(3)的运行速度与(1)一样快(没有调试器时),而只有情况(4)慢一些?

    来自C++ Debugging Improvements in Visual Studio "14"


    因此,为了在使用以下命令启动C ++应用程序时提高性能,Visual Studio调试器,在Visual Studio 2015中我们禁用该操作系统的调试堆。

    这是通过在调试过程环境中设置

    _NO_DEBUG_HEAP=1完成的。因此,请比较Accelerating Debug Runs, Part 1: _NO_DEBUG_HEAP(文章较旧)-现在默认为[]。

    我们可以通过应用程序中的下一个代码进行检查:

    WCHAR _no_debug_heap[32]; if (GetEnvironmentVariable(L"_NO_DEBUG_HEAP", _no_debug_heap, _countof(_no_debug_heap))) { DbgPrint("_NO_DEBUG_HEAP=%S\n", _no_debug_heap); } else { DbgPrint("error=%u\n", GetLastError()); }

    所以当我们在调试器下启动应用程序时-没有调试堆,因为VS调试器添加了_NO_DEBUG_HEAP=1。当您从调试器启动调试器和应用程序下的调试器时-从CreateProcessW函数

    lpEnvironment

    ] >>
    指向新进程的环境块的指针。如果这参数为NULL,新进程使用调用环境过程。

    因为您在此处传递0-因此应用使用与调试器相同的环境-继承CreateProcessW

    但在情况(4)中-您不能自行设置_NO_DEBUG_HEAP=1。结果使用了调试堆并且运行速度更慢。
    © www.soinside.com 2019 - 2024. All rights reserved.