Есть ли способ программно проверить, сколько ядер использует приложение C ++?
я ищу Windows/Linux
решение, но, конечно, решение, не зависящее от платформы, было бы предпочтительным, я думаю, что оно требует слишком многого.
Нет способа узнать, сколько ядер использует приложение. Но вы можете Угадай это по количеству потоков, которые он имеет.
Для окон:
Вы хотите использовать Библиотека справки по инструментам как называет это майкрософт Точнее, вы захотите взглянуть на Обход списка тем пример, который может получить количество потоков в приложении.
Microsoft действительно любит делать их примеры настолько уродливыми, насколько это возможно, так что вот моя версия с предварительным подтверждением, которую я придумал, вы предоставляете ей PID, и в ней перечисляются все связанные с ней потоки:
#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;
}
Входные данные:
5532
(идентификатор процесса обслуживания Steam для меня), GetCurrentProcessId()
Выход:
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
Вы можете предположить, что если приложение использует больше потоков, чем число ядер, которое имеет процессор, оно, вероятно, использует их все, а если оно использует меньше, оно, вероятно, использует x количество ядер, где x — количество потоков.
Если вы хотите пойти еще дальше, вы можете получить использование процессора для каждого потока, чтобы сделать лучше приближение сколько ядер он использует.
Другой подход, который я не совсем уверен, сработает, это взять загрузку ЦП всех потоков приложения и сложить их (в процентах), взять количество ядер в системе, увеличить это число до степени — 1 и умножьте это на 100 (x^-1*100
) где x — количество ядер, а затем разделите процент использования ЦП всеми потоками на процент того, сколько ядро может обработать, чтобы приблизительно определить, сколько ядер оно использует.
Например:
Учитывая 4 ядра и приложение с 4 потоками, 2 из них имеют загрузку процессора 25%, а другие 2 — по 11%.
Можно предположить, что он использует:
(25 + 25 + 11 + 11) / ((4 ^ -1) * 100) = 2,88 ядра
Эта проблема:
Возможно, что не все ядра работают с одинаковой скоростью. В этом случае его не будет работать, как задумано.
Если вы используете c ++ 11, вы можете узнать количество ядер, с которыми система имеет std::thread::hardware_concurrency()
,
В качестве альтернативы вы также можете пройти список процессов и получить количество потоков, которые процесс имеет оттуда, но у него нет расширенной информации о каждом потоке, как при прохождении потоков.
Я собираюсь сделать второй ответ здесь, потому что последний уже достаточно длинный, и этот ответ будет идти в несколько ином направлении.
После дальнейших исследований я определил, что на самом деле есть способ точно определить, на каком ядре / может / работает каждый поток. Код, который я придумал, использует специфичные для Windows библиотеки, но, безусловно, есть функции, эквивалентные Linux.
Более конкретно его использование wbemuuid.lib
, comdef.h
а также Wbemidl.h
,
Код:
#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;
}
Выходы, когда Prime95 остановлен:
IDThread : 9072 : 15
IDThread : 7052 : 15
Выводится, когда Prime95 работает с 4 рабочими потоками:
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
Выводится, когда Prime95 работает с 2 рабочими потоками:
IDThread : 9072 : 15
IDThread : 7052 : 15
IDThread : 2352 : 15
IDThread : 8396 : 15
Объяснение:
Чтобы объяснить код немного:
7424
это PID Prime95.SELECT * FROM Win32_PerfFormattedData_PerfProc_Thread WHERE IDProcess = 7424
это запрос, который я использую, чтобы перечислить все темы, связанные с конкретным PID. Вы можете найти список всей информации, которую вы можете получить от Win32_PerfFormattedData_PerfProc_Thread
Вот. Все, что вам нужно сделать, это переключить аргумент parse()
от ThreadID
чтобы сказать PercentProcessorTime
например, и он собирается вывести процент использования процессора.Affinity:
Функция affinity()
устанавливает привязку потока к новому, чтобы получить старый, а затем устанавливает его обратно к старому. Теперь я не уверен, как получить фактический номер ядра из сходства, что я знаю, что если его, например, 1
он работает на ядре номер 1, если его 2
он работает с ядром № 2, если его 7
он работает на ядрах 4 и 3 или что-то в этом роде. Я еще не до конца разобрался.
Портирование на Linux:
В Linux все немного проще, например, получить ядро можно с помощью чего-то вроде sched_getcpu
/sched_getaffinity
. Немного погуглив, я уверен, что вы можете найти метод, чтобы вывести список всех потоков, связанных с процессом.