最近为项目编写基准性能测试,需要在日志中收集运行环境的硬件信息。这里将分别介绍在 Windows 和 Linux 环境中,如何使用 C++ 获取 CPU 型号、GPU 型号、物理内存(RAM)大小以及显存(VRAM)大小。值得注意的是,无法直接通过 C++ 标准库获取这些底层硬件信息,因此需要依赖于操作系统提供的 API 或特定的第三方库。

获取 CPU 信息

在 Windows 环境下,一般使用 __cpuid 内联函数,通过直接调用 CPU 的 cpuid 指令来获取信息。这种方法需要包含 <intrin.h> 头文件。这个头文件的核心功能是为开发者提供“内联函数”(Intrinsics)的访问接口。这些内联函数允许程序员在 C/C++ 代码中直接使用特定的处理器指令,而无需编写汇编代码。编译器在编译时会将这些函数调用替换为相应的、高度优化的汇编指令。需要注意的是,<intrin.h>是由 MSVC 实现的头文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <array>

int main() {
std::array<int, 4> cpuInfo;
char cpuBrandString[0x41];
memset(cpuBrandString, 0, sizeof(cpuBrandString));

for (unsigned int i = 0x80000002; i <= 0x80000004; ++i) {
__cpuid(cpuInfo.data(), i);
memcpy(cpuBrandString + (i - 0x80000002) * 16, cpuInfo.data(), 16);
}

std::cout << "CPU Model: " << cpuBrandString << std::endl;

return 0;
}

在 Linux 环境中,获取 CPU 信息通常通过解析 /proc/cpuinfo 这个文件来实现。此文件包含了关于 CPU 的详细信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <fstream>
#include <string>

int main() {
std::ifstream cpuinfo("/proc/cpuinfo");
std::string line;
std::string modelName;

if (cpuinfo.is_open()) {
while (getline(cpuinfo, line)) {
if (line.find("model name") != std::string::npos) {
modelName = line.substr(line.find(":") + 2);
break;
}
}
cpuinfo.close();
}

if (!modelName.empty()) {
std::cout << "CPU Model: " << modelName << std::endl;
} else {
std::cerr << "Could not retrieve CPU model." << std::endl;
}

return 0;
}

获取物理内存(RAM)大小

Windows API 提供了 GlobalMemoryStatusEx 函数,可以获取当前环境的内存状况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <windows.h>

int main() {
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);

if (GlobalMemoryStatusEx(&statex)) {
std::cout << "Total Physical Memory: "
<< statex.ullTotalPhys / (1024 * 1024)
<< " MB" << std::endl;
} else {
std::cerr << "Failed to retrieve memory information." << std::endl;
}

return 0;
}

在Linux上,可以通过解析 /proc/meminfo 文件来获取内存大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <fstream>
#include <string>

int main() {
std::ifstream meminfo("/proc/meminfo");
std::string line;
long long memTotal = 0;

if (meminfo.is_open()) {
while (getline(meminfo, line)) {
if (line.find("MemTotal") != std::string::npos) {
memTotal = std::stoll(line.substr(line.find(":") + 1));
break;
}
}
meminfo.close();
}

if (memTotal > 0) {
std::cout << "Total Physical Memory: " << memTotal / 1024 << " MB" << std::endl;
} else {
std::cerr << "Could not retrieve memory information." << std::endl;
}

return 0;
}

获取 GPU 信息和显存容量

GPU 信息的获取需要配合图形 API(OpenGL/Vulkan)。应用程序需要初始化相应的图形 API 才能查询到这些信息。

使用 OpenGL 获取 GPU 型号相对直接,但获取显存容量则依赖于厂商的特定扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

int main() {
if (!glfwInit()) {
return -1;
}

GLFWwindow* window = glfwCreateWindow(640, 480, "Hardware Info", NULL, NULL);
if (!window) {
glfwTerminate();
return -1;
}

glfwMakeContextCurrent(window);
glewInit();

const GLubyte* renderer = glGetString(GL_RENDERER);
const GLubyte* vendor = glGetString(GL_VENDOR);

std::cout << "GPU Vendor: " << vendor << std::endl;
std::cout << "GPU Model: " << renderer << std::endl;

// 获取显存大小需要使用扩展
// NVIDIA: GL_NVX_gpu_memory_info
// AMD: GL_ATI_meminfo
GLint total_mem_kb = 0;
if (glewIsSupported("GL_NVX_gpu_memory_info")) {
glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &total_mem_kb);
} else if (glewIsSupported("GL_ATI_meminfo")) {
// AMD扩展返回的是一个包含四个值的数组
// total, total-free, aux, aux-free in KB
GLint mem_info[4];
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, mem_info);
total_mem_kb = mem_info[0];
}

if (total_mem_kb > 0) {
std::cout << "VRAM Size: " << total_mem_kb / 1024 << " MB" << std::endl;
} else {
std::cout << "VRAM size could not be determined via standard OpenGL extensions." << std::endl;
}


glfwDestroyWindow(window);
glfwTerminate();

return 0;
}

Vulkan 提供了详细的物理设备属性查询功能且为核心特性,因此平台无关。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <vulkan/vulkan.h>

int main() {
VkInstance instance;
// ... 创建Vulkan实例 ...

uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());

if (deviceCount > 0) {
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(devices[0], &deviceProperties);
std::cout << "GPU: " << deviceProperties.deviceName << std::endl;

VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(devices[0], &memProperties);

VkDeviceSize vramSize = 0;
for (uint32_t i = 0; i < memProperties.memoryHeapCount; ++i) {
if (memProperties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
vramSize += memProperties.memoryHeaps[i].size;
}
}
std::cout << "VRAM Size: " << vramSize / (1024 * 1024) << " MB" << std::endl;
}

// ... 清理Vulkan资源 ...
return 0;
}