如何确定一台计算机的硬件配置(CPU和RAM)?

21

我正在开发一个跨平台的性能测试套件,并希望在每次测试报告中添加有关计算机CPU(架构/时钟速度/核心数)和RAM(总量)的信息。目前,我需要针对Windows和Unix两个平台获取这些信息,有什么方法可以获得吗?

编辑:感谢大家提供的答案,现在我已经获得了CPU架构、CPU核心数和总内存,但是我仍然缺少CPU时钟速度的信息。对于这个问题有什么想法吗?


__cpuinfo 返回的字符串包括时钟速度。 - bsruth
@bsruth 谢谢,现在我明白它确实在品牌字符串中。 - Robert Gould
https://github.com/lfreist/hwinfo 目前正在开发中,但已经涵盖了Linux和Windows的大部分信息。CPU信息也适用于MacOS。 - prog2de
13个回答

14

下面是一种在Windows机器上获取所需信息的方法。我从一个实际项目中复制并粘贴了它,并进行了一些小修改,所以请随意清理它以使其更易理解。

        int CPUInfo[4] = {-1};
        unsigned   nExIds, i =  0;
        char CPUBrandString[0x40];
        // Get the information associated with each extended ID.
        __cpuid(CPUInfo, 0x80000000);
        nExIds = CPUInfo[0];
        for (i=0x80000000; i<=nExIds; ++i)
        {
            __cpuid(CPUInfo, i);
            // Interpret CPU brand string
            if  (i == 0x80000002)
                memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
            else if  (i == 0x80000003)
                memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
            else if  (i == 0x80000004)
                memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
        }
        //string includes manufacturer, model and clockspeed
        cout << "CPU Type: " << CPUBrandString << endl;


        SYSTEM_INFO sysInfo;
        GetSystemInfo(&sysInfo);
        cout << "Number of Cores: " << sysInfo.dwNumberOfProcessors << endl;

        MEMORYSTATUSEX statex;
        statex.dwLength = sizeof (statex);
        GlobalMemoryStatusEx(&statex);
        cout << "Total System Memory: " << (statex.ullTotalPhys/1024)/1024 << "MB" << endl;

更多信息请查看GetSystemInfoGlobalMemoryStatusEx__cpuid。虽然我没有包含它,但你也可以通过GetSystemInfo函数确定操作系统是32位还是64位。


10

10

很抱歉来晚了,但这是我的贡献。 我尝试采用更现代的C++方法。

    #include <intrin.h> // NOTE this header is MSVC specific!
    #include <string>
    #include <array>

    std::string GetCpuInfo()
    {
        // 4 is essentially hardcoded due to the __cpuid function requirements.
        // NOTE: Results are limited to whatever the sizeof(int) * 4 is...
        std::array<int, 4> integerBuffer = {};
        constexpr size_t sizeofIntegerBuffer = sizeof(int) * integerBuffer.size();

        std::array<char, 64> charBuffer = {};

        // The information you wanna query __cpuid for.
        // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=vs-2019
        constexpr std::array<int, 3> functionIds = {
            // Manufacturer
            //  EX: "Intel(R) Core(TM"
            0x8000'0002,
            // Model
            //  EX: ") i7-8700K CPU @"
            0x8000'0003,
            // Clockspeed
            //  EX: " 3.70GHz"
            0x8000'0004
        };

        std::string cpu;

        for (int id : functionIds)
        {
            // Get the data for the current ID.
            __cpuid(integerBuffer.data(), id);
            
            // Copy the raw data from the integer buffer into the character buffer
            std::memcpy(charBuffer.data(), integerBuffer.data(), sizeofIntegerBuffer);

            // Copy that data into a std::string
            cpu += std::string(charBuffer.data());
        }

        return cpu;
    }

以下是该函数对我的结果:"Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz"

老实说,这样的东西没有被标准化真的很恼人...


2
一直在寻找获取与MSInfo中相同的CPU信息的方法,但是我找不到。我将其复制、粘贴并在30秒内使其工作。这个函数非常好,谢谢! - Skewjo

9

对于使用GCC的Linux,您可以像在Windows上一样使用非常相似的解决方案。您需要包括<cpuid.h>并根据此处修改__cpuid()方法的输入。

#include <cpuid.h>

char CPUBrandString[0x40];
unsigned int CPUInfo[4] = {0,0,0,0};

__cpuid(0x80000000, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
unsigned int nExIds = CPUInfo[0];

memset(CPUBrandString, 0, sizeof(CPUBrandString));

for (unsigned int i = 0x80000000; i <= nExIds; ++i)
{
    __cpuid(i, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);

    if (i == 0x80000002)
        memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
    else if (i == 0x80000003)
        memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
    else if (i == 0x80000004)
        memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
}

cout << "CPU Type: " << CPUBrandString << endl;

7

在Windows中确定CPU时钟速度的方法:

double CPUSpeed()
{
    wchar_t Buffer[_MAX_PATH];
    DWORD BufSize = _MAX_PATH;
    DWORD dwMHz = _MAX_PATH;
    HKEY hKey;

    // open the key where the proc speed is hidden:
    long lError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                                L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
                                0,
                                KEY_READ,
                                &hKey);
    if(lError != ERROR_SUCCESS)
    {// if the key is not found, tell the user why:
        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
                        NULL,
                        lError,
                        0,
                        Buffer,
                        _MAX_PATH,
                        0);
        wprintf(Buffer);
        return 0;
    }

    // query the key:
    RegQueryValueEx(hKey, L"~MHz", NULL, NULL, (LPBYTE) &dwMHz, &BufSize);
    return (double)dwMHz;
}

6

CPU很容易。使用cpuid指令即可。我会让其他帖子的作者找到一种便携式的方法来确定系统有多少RAM。 :-)

对于Linux特定的方法,您可以访问/proc/meminfo(如果您不想解析cpuid响应,则可以访问/proc/cpuinfo)。


1
而且,如果处理器没有任何CPUID指令,预计如何在处理器上找到CPU信息? ;)(即使使用CPUID,你能知道处理器封装有几个内核吗?) - araqnid
1
阅读:http://www.intel.com/design/processor/applnots/241618.htm(简短回答:是的)。但如果CPU过于老旧而不支持cpuid,则所有赌注都将失效,但是现代系统没有这个问题。 - C. K. Young
1
我在思考并非所有系统都基于i386架构处理器。(尽管意识到这可能不是原帖作者感兴趣的情况) - araqnid
呃,全世界都是x86 / x64!:-P(开玩笑的)。 - C. K. Young

5
在Linux上,您可以解析/proc/cpuinfo(包含每个处理器的信息块)和/proc/meminfo(包含各种常规内存统计信息,包括MemTotal)。

2
这段文字的意思是:bsruth在这个页面上给出的最被接受的答案使用了__cpuid循环,不必要地遍历了不需要的扩展功能。如果你只需要知道处理器品牌字符串,那么没有必要查询0x80000000。
维基百科有一个很好的解释和示例代码: https://en.wikipedia.org/wiki/CPUID#EAX=80000002h,80000003h,80000004h:_Processor_Brand_String
#include <cpuid.h>  // GCC-provided
#include <stdio.h>
#include <stdint.h>

int main(void) {
    uint32_t brand[12];

    if (!__get_cpuid_max(0x80000004, NULL)) {
        fprintf(stderr, "Feature not implemented.");
        return 2;
    }

    __get_cpuid(0x80000002, brand+0x0, brand+0x1, brand+0x2, brand+0x3);
    __get_cpuid(0x80000003, brand+0x4, brand+0x5, brand+0x6, brand+0x7);
    __get_cpuid(0x80000004, brand+0x8, brand+0x9, brand+0xa, brand+0xb);
    printf("Brand: %s\n", brand);
}

这里是我想出来的直接将其转换为C++中的std::string版本。
std::string CPUBrandString;
CPUBrandString.resize(49);
uint *CPUInfo = reinterpret_cast<uint*>(CPUBrandString.data());
for (uint i=0; i<3; i++)
    __cpuid(0x80000002+i, CPUInfo[i*4+0], CPUInfo[i*4+1], CPUInfo[i*4+2], CPUInfo[i*4+3]);
CPUBrandString.assign(CPUBrandString.data()); // correct null terminator
std::cout << CPUBrandString << std::endl;

这个版本是为Linux设计的,但使用 __cpuid 应该不难在Windows上找到解决方法。

1

楼主想要一个在Windows和Linux之间可移植的CPU时钟速度计算程序。这里是:

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef unsigned __int64 usCount;
static usCount GetUsCount()
{
    static LARGE_INTEGER ticksPerSec;
    static double scalefactor;
    LARGE_INTEGER val;
    if(!scalefactor)
    {
        if(QueryPerformanceFrequency(&ticksPerSec))
            scalefactor=ticksPerSec.QuadPart/1000000000000.0;
        else
            scalefactor=1;
    }
    if(!QueryPerformanceCounter(&val))
        return (usCount) GetTickCount() * 1000000000;
    return (usCount) (val.QuadPart/scalefactor);
}
#else
#include <sys/time.h>
#include <time.h>
#include <sched.h>
typedef unsigned long long usCount;
static usCount GetUsCount()
{
#ifdef CLOCK_MONOTONIC
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ((usCount) ts.tv_sec*1000000000000LL)+ts.tv_nsec*1000LL;
#else
    struct timeval tv;
    gettimeofday(&tv, 0);
    return ((usCount) tv.tv_sec*1000000000000LL)+tv.tv_usec*1000000LL;
#endif
}
#endif
static usCount usCountOverhead, CPUClockSpeed;
#ifdef __GNUC__
#include "x86intrin.h"
#define __rdtsc() __builtin_ia32_rdtsc()
#endif
static usCount GetClockSpeed()
{
  int n;
  usCount start, end, start_tsc, end_tsc;
  if(!usCountOverhead)
  {
    usCount foo=0;
    start=GetUsCount();
    for(n=0; n<1000000; n++)
    {
      foo+=GetUsCount();
    }
    end=GetUsCount();
    usCountOverhead=(end-start)/n;
  }
  start=GetUsCount();
  start_tsc=__rdtsc();
  for(n=0; n<1000; n++)
#ifdef WIN32
    Sleep(0);
#else
    sched_yield();
#endif
  end_tsc=__rdtsc();
  end=GetUsCount();
  return (usCount)((1000000000000.0*(end_tsc-start_tsc))/(end-start-usCountOverhead));
}

显然,这仅适用于x86 / x64,并且依赖于TSC以与CPU相同的速度计数。如果您进行了奇怪的超频操作,例如在我的电脑上,我超频FSB但降低倍频器以保持核心时钟在规格上,因此TSC将计算FSB乘以最大倍频器,这太快了。

为了获得最佳结果,在运行GetClockSpeed()之前,建议您运行反SpeedStep循环,例如

usCount start;
start=GetUsCount();
while(GetUsCount()-start<3000000000000ULL);
CPUClockSpeed=GetClockSpeed();

Niall


1
在Solaris上:
-对于内存
 prtconf | grep Memory

-针对中央处理器

 psrinfo -v | grep MHz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接