Programmatically check the number of kernel applications

Is there a way to programmatically check how many cores a C ++ application uses?

I am looking for a Windows/Linux solution, but, of course, a platform-independent solution would be preferable, I think they ask him too much.

+6
source share
2 answers

It is not possible to know how many cores the application is using. But you can guess its number of threads that it has.

For windows:

You will want to use the Toolkit Tool Library , as Microsoft calls it. More specifically, you might want to take a look at the example of "Moving a topic list", which can get the number of threads an application has.

Microsoft really loves to make its examples as ugly as they could be, so I had a faithful version that I came up with, you pass it the PID and all the threads associated with it are listed:

 #include <windows.h> #include <tlhelp32.h> #include <tchar.h> #include <cstdio> bool list(unsigned int PID); int main(void) { list(5532); list(GetCurrentProcessId()); return 0; } bool list(unsigned int PID) { HANDLE thread_snap = INVALID_HANDLE_VALUE; THREADENTRY32 te32; // Take a snapshot of all running threads thread_snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (thread_snap == INVALID_HANDLE_VALUE) return false; // Fill in the size of the structure before using it. te32.dwSize = (DWORD)sizeof(THREADENTRY32); // Retrieve information about the first thread, and exit if unsuccessful if (!Thread32First(thread_snap, &te32)) { CloseHandle(thread_snap); return false; } // Now walk the thread list of the system, and display information about each thread associated with the specified process printf("Printing threads for PID %u\n", PID); do { if (te32.th32OwnerProcessID == PID) { printf( "THREAD ID = 0x%08X with base priority %u and delta priority %u\n", (unsigned int)te32.th32ThreadID, (unsigned int)te32.tpBasePri, (unsigned int)te32.tpDeltaPri); } } while (Thread32Next(thread_snap, &te32)); printf("Done printing threads for PID %u\n\n", PID); // Don't forget to clean up the snapshot object. CloseHandle(thread_snap); return true; } 

Input:

5532 (steam service process id for me), GetCurrentProcessId()

Output:

 Printing threads for PID 5532 THREAD ID = 0x00000BCC with base priority 8 and delta priority 0 THREAD ID = 0x0000041C with base priority 8 and delta priority 0 THREAD ID = 0x00001924 with base priority 8 and delta priority 0 THREAD ID = 0x00000C9C with base priority 8 and delta priority 0 Done printing threads for PID 5532 Printing threads for PID 9836 THREAD ID = 0x000000FC with base priority 8 and delta priority 0 Done printing threads for PID 9836 

It can be assumed that if the application uses more threads than the number of cores, then the processor probably uses all of them, and if it uses less, it probably uses x the number of cores, where x is the number of threads.

If you want to go even further, you can get CPU usage for each thread to better approximate the number of cores it uses.


Another approach, which I'm not entirely sure of, will be to use the processor for all application threads and add them (in percent), take the number of cores that the system has, raise this number to a power of -1 and multiply by 100 ( x^-1*100 ), where x is the number of cores, and then divide the percentage of CPU usage of all threads by the percentage of how many cores can process in order to get closer to the number of cores that it uses.

For instance:

Considering 4 cores and an application with 4 threads, 2 of them have 25% of CPU usage and the other 2 of 11%.

It can be assumed that it uses:

(25 + 25 + 11 + 11) / ((4 ^ -1) * 100) = 2.88 cores

Problem:

It is possible that not all cores are synchronized at the same speed. In this case, it will not work as intended.


If you are using C ++ 11, you can find out the number of cores having a system with std::thread::hardware_concurrency() .

Alternatively, you can also cross the list of processes and get the number of threads that the process has, but it does not have additional information about each thread, such as thread bypass.

+3
source

Now we need to make the second answer, because the latter is already quite long, and this answer will go in a slightly different direction.

After further research, I decided that there really is a way to figure out which core of each thread is working / working. The code I came up with uses a lot of Windows-specific libraries, but there certainly are equivalent Linux features there.

More specifically, this uses wbemuuid.lib , comdef.h and Wbemidl.h .


Code:

 #define _WIN32_DCOM #include <iostream> #include <comdef.h> #include <Wbemidl.h> #include <cstdarg> #include <string> #pragma comment(lib, "wbemuuid.lib") using namespace std; DWORD affinity(unsigned int ID) { HANDLE threadh = OpenThread(THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION, FALSE, ID); DWORD mask = 1; DWORD old = 0; while (mask) { old = SetThreadAffinityMask(threadh, mask); if (old) { SetThreadAffinityMask(threadh, old); return old; } else { if (GetLastError() != ERROR_INVALID_PARAMETER) return 0; } mask <<= 1; } return 0; } HRESULT connect(IWbemLocator** pLoc, IWbemServices** pSvc) { HRESULT hres; hres = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) { cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl; return hres; } hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); if (FAILED(hres)) { cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl; CoUninitialize(); return hres; } hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&(*pLoc)); if (FAILED(hres)) { cout << "Failed to create IWbemLocator object." << " Error code = 0x" << hex << hres << endl; CoUninitialize(); return hres; } hres = (*pLoc)->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &(*pSvc)); if (FAILED(hres)) { cout << "Could not connect. Error code = 0x" << hex << hres << endl; (*pLoc)->Release(); CoUninitialize(); return hres; } hres = CoSetProxyBlanket((*pSvc), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); if (FAILED(hres)) { cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl; (*pSvc)->Release(); (*pLoc)->Release(); CoUninitialize(); return hres; } return hres; } HRESULT query(IWbemLocator** pLoc, IWbemServices** pSvc, IEnumWbemClassObject** pEnum, const char* qry) { HRESULT hres; hres = (*pSvc)->ExecQuery(bstr_t("WQL"), bstr_t(qry), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &(*pEnum)); if (FAILED(hres)) { cout << "Query for operating system name failed." << " Error code = 0x" << hex << hres << endl; (*pSvc)->Release(); (*pLoc)->Release(); CoUninitialize(); return 1; } return hres; } HRESULT parse(IWbemLocator** pLoc, IWbemServices** pSvc, IEnumWbemClassObject** pEnum, IWbemClassObject** pCls, size_t n_args, ...) { HRESULT hres; ULONG uReturn = 0; while (pEnum) { hres = (*pEnum)->Next(WBEM_INFINITE, 1, &(*pCls), &uReturn); if (0 == uReturn) { break; } VARIANT vtProp; va_list vl; va_start(vl, n_args); for (size_t i = 0; i < n_args; i++) { const char* name = va_arg(vl, const char*); int wchars_num = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0); wchar_t* wname = new wchar_t[wchars_num]; MultiByteToWideChar(CP_UTF8 , 0, name, -1, wname, wchars_num); hres = (*pCls)->Get(wname, 0, &vtProp, 0, 0); wcout << wname << " : " << std::to_wstring((size_t)vtProp.bstrVal) << " : " << affinity((DWORD)vtProp.bstrVal) << endl; delete[] wname; } va_end(vl); VariantClear(&vtProp); } return hres; } int main(int argc, char **argv) { string qry = "SELECT * FROM Win32_PerfFormattedData_PerfProc_Thread WHERE IDProcess = 7424"; HRESULT hres; IWbemLocator* pLoc = NULL; IWbemServices* pSvc = NULL; IEnumWbemClassObject* pEnum = NULL; IWbemClassObject* pCls = NULL; hres = connect(&pLoc, &pSvc); if (FAILED(hres)) return 1; hres = query(&pLoc, &pSvc, &pEnum, qry.c_str()); if (FAILED(hres)) return 1; hres = parse(&pLoc, &pSvc, &pEnum, &pCls, 1, "IDThread"); if (FAILED(hres)) return 1; pSvc->Release(); pLoc->Release(); pEnum->Release(); pCls->Release(); CoUninitialize(); return 0; } 

Outputs when Prime95 is stopped:

 IDThread : 9072 : 15 IDThread : 7052 : 15 

Conclusion when Prime95 works with 4 workflows:

 IDThread : 9072 : 15 IDThread : 7052 : 15 IDThread : 5600 : 1 IDThread : 5888 : 2 IDThread : 2888 : 4 IDThread : 9348 : 8 PercentProcessorTime : 0 PercentProcessorTime : 0 PercentProcessorTime : 70 PercentProcessorTime : 83 PercentProcessorTime : 80 PercentProcessorTime : 75 

Conclusion when Prime95 works with two workflows:

 IDThread : 9072 : 15 IDThread : 7052 : 15 IDThread : 2352 : 15 IDThread : 8396 : 15 

Explanation:

To explain the code a bit:

  • 7424 is the PID for Prime95.
  • SELECT * FROM Win32_PerfFormattedData_PerfProc_Thread WHERE IDProcess = 7424 is a query that I use to list all threads related to a specific PID. You can find a list of all the information you can get from Win32_PerfFormattedData_PerfProc_Thread here . All you have to do is switch the argument assigned by parse() from ThreadID to say, for example, PercentProcessorTime , and it will output the percentage of CPU usage.
  • The code is very ugly and possibly unsafe, its also a heavily modified version Example: receiving WMI data from a local computer from MSDN.

Affinity:

The affinity() function sets the thread to the new one to get the old one, and then returns it to the old one. Now I'm not sure how to get the actual number of the kernel from proximity, I know that if it, for example, 1 it runs on core number 1, if its 2 it runs core number 2, if its 7 it runs on core 4 and 3 or something like that. I have yet to fully understand this.


Porting it to linux:

On linux, this is all a little easier, for example, getting the kernel can be done using sched_getcpu / sched_getaffinity . With a little googling, I'm sure you can find a method to list all the threads associated with a process.

+3
source

Source: https://habr.com/ru/post/946009/


All Articles