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.