Программно проверить количество ядер, используемых приложением

Есть ли способ программно проверить, сколько ядер использует приложение C ++?

я ищу Windows/Linux решение, но, конечно, решение, не зависящее от платформы, было бы предпочтительным, я думаю, что оно требует слишком многого.

6

Решение

Нет способа узнать, сколько ядер использует приложение. Но вы можете Угадай это по количеству потоков, которые он имеет.

Для окон:

Вы хотите использовать Библиотека справки по инструментам как называет это майкрософт Точнее, вы захотите взглянуть на Обход списка тем пример, который может получить количество потоков в приложении.

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(),

В качестве альтернативы вы также можете пройти список процессов и получить количество потоков, которые процесс имеет оттуда, но у него нет расширенной информации о каждом потоке, как при прохождении потоков.

3

Другие решения

Я собираюсь сделать второй ответ здесь, потому что последний уже достаточно длинный, и этот ответ будет идти в несколько ином направлении.

После дальнейших исследований я определил, что на самом деле есть способ точно определить, на каком ядре / может / работает каждый поток. Код, который я придумал, использует специфичные для 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 например, и он собирается вывести процент использования процессора.
  • Код очень уродливый и, возможно, небезопасный, это также сильно измененная версия Пример: получение данных WMI с локального компьютера из MSDN.

Affinity:

Функция affinity() устанавливает привязку потока к новому, чтобы получить старый, а затем устанавливает его обратно к старому. Теперь я не уверен, как получить фактический номер ядра из сходства, что я знаю, что если его, например, 1 он работает на ядре номер 1, если его 2 он работает с ядром № 2, если его 7 он работает на ядрах 4 и 3 или что-то в этом роде. Я еще не до конца разобрался.


Портирование на Linux:

В Linux все немного проще, например, получить ядро ​​можно с помощью чего-то вроде sched_getcpu/sched_getaffinity. Немного погуглив, я уверен, что вы можете найти метод, чтобы вывести список всех потоков, связанных с процессом.

3

По вопросам рекламы [email protected]