在 Windows 环境下使用 MSVC 编译器cl.exe测试 int32_t 和 char 的性能

在 Windows 环境下使用 MSVC 编译器cl.exe测试 int32_t 和 char 的性能

在 Windows 环境下使用 MSVC 编译器(cl.exe)测试 int32_t 和 char 的性能,思路与 Linux 类似,但需适配 Windows 的计时函数和编译方式。以下是具体实现和说明:

核心差异点

  • 计时函数:Windows 中使用 QueryPerformanceCounter(高精度计时器,精度一般为微秒级),替代 Linux 的 clock_gettime。
  • 编译工具:使用 Visual Studio 的 cl.exe 编译器,优化选项为 /O2(对应 GCC 的 -O2)。
  • 数据类型与头文件:需包含 Windows 特定头文件(如 windows.h),int32_t 仍通过 stdint.h 声明。

测试代码(Windows + MSVC)

c

运行

#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#include <string.h>

// 计时工具函数:返回当前高精度计数器值
static LARGE_INTEGER get_counter() {
    LARGE_INTEGER counter;
    QueryPerformanceCounter(&counter);
    return counter;
}

// 计算两个计数器值的时间差(单位:纳秒)
static uint64_t get_elapsed_ns(LARGE_INTEGER start, LARGE_INTEGER end) {
    LARGE_INTEGER freq;
    QueryPerformanceFrequency(&freq); // 获取计数器频率(每秒跳动次数)
    // 时间差(秒)= (end - start) / 频率,转换为纳秒(×1e9)
    return (uint64_t)((end.QuadPart - start.QuadPart) * 1000000000.0 / freq.QuadPart);
}

// 测试1:基本运算性能(累加)
void test_operation() {
    const int iterations = 1000000000; // 10亿次运算
    LARGE_INTEGER start, end;

    // 测试 int32_t
    volatile int32_t a = 0; // volatile 防止编译器优化掉循环
    start = get_counter();
    for (int i = 0; i < iterations; i++) {
        a += 1;
    }
    end = get_counter();
    printf("int32_t 运算耗时:%llu ns
", get_elapsed_ns(start, end));

    // 测试 char(signed char,与 int32_t 符号一致)
    volatile char b = 0;
    start = get_counter();
    for (int i = 0; i < iterations; i++) {
        b += 1;
    }
    end = get_counter();
    printf("char 运算耗时:%llu ns
", get_elapsed_ns(start, end));

    // 避免“未使用变量”警告(实际a和b已被volatile修饰,不会被优化)
    (void)a;
    (void)b;
}

// 测试2:数组遍历性能
void test_array() {
    const int size = 1000000; // 100万个元素
    LARGE_INTEGER start, end;

    // 分配 int32_t 数组并初始化
    int32_t* arr32 = (int32_t*)malloc(size * sizeof(int32_t));
    memset(arr32, 0, size * sizeof(int32_t));

    // 测试 int32_t 数组遍历
    volatile int32_t sum32 = 0;
    start = get_counter();
    for (int i = 0; i < size; i++) {
        sum32 += arr32[i];
    }
    end = get_counter();
    printf("int32_t 数组遍历耗时:%llu ns
", get_elapsed_ns(start, end));

    // 分配 char 数组并初始化
    char* arr8 = (char*)malloc(size * sizeof(char));
    memset(arr8, 0, size * sizeof(char));

    // 测试 char 数组遍历
    volatile char sum8 = 0;
    start = get_counter();
    for (int i = 0; i < size; i++) {
        sum8 += arr8[i];
    }
    end = get_counter();
    printf("char 数组遍历耗时:%llu ns
", get_elapsed_ns(start, end));

    (void)sum32;
    (void)sum8;
    free(arr32);
    free(arr8);
}

int main() {
    printf("=== 基本运算测试 ===
");
    test_operation();
    printf("
=== 数组遍历测试 ===
");
    test_array();
    return 0;
}

编译与运行步骤

  1. 打开 Developer Command Prompt for VS(确保 cl.exe 在环境变量中)。路径示例:C:Program FilesMicrosoft Visual Studio2022CommunityCommon7ToolsVsDevCmd.bat
  2. 编译命令(启用优化):
  3. cmd
  4. cl /O2 test.c /Fe:test.exe
  5. /O2:启用最高级优化(对应 GCC 的 -O2),模拟实际发布环境。
  6. /Fe:test.exe:指定输出可执行文件名。
  7. 运行程序
  8. cmd
  9. test.exe

关键注意事项

  1. 防止编译器优化:MSVC 的优化器(尤其是 /O2 级别)可能会删除 “无副作用” 的循环(如纯累加未使用的变量)。因此必须用 volatile 修饰循环中修改的变量(如 a、b、sum32、sum8),强制编译器执行循环。
  2. 计时精度:QueryPerformanceCounter 的精度依赖硬件(一般与 CPU 时钟同步),比 clock() 或 GetTickCount() 高得多,适合微秒级甚至纳秒级的时间测量。
  3. 测试场景扩展:可增加更多测试(如乘法、位运算、随机访问数组),例如将 a += 1 改为 a = (a * 3 + 1) % 100 模拟复杂运算。
  4. 汇编代码分析:若需查看编译器生成的指令差异,可添加 /FAs 选项生成汇编代码:
  5. cmd
  6. cl /O2 /FAs test.c
  7. 生成的 test.asm 文件中可对比 int32_t 和 char 对应的汇编指令(如 char 运算是否有 movsx 符号扩展指令)。

预期结果参考

在 x86_64 架构的 Windows 系统(如 Intel i5/i7 处理器)、MSVC /O2 优化下:

  • 基本运算:int32_t 一般比 char 快 10%-20%,因 char 累加可能需要 movsx eax, cl 等符号扩展指令(将 8 位寄存器扩展到 32 位),而 int32_t 可直接使用 add eax, 1 等指令。
  • 数组遍历:若数组大小超过 CPU 缓存(如 100 万元素),char 数组因内存占用更小(1/4 于 int32_t),缓存命中率更高,可能反超 int32_t 的速度;若数组较小(如 1 万元素),int32_t 因对齐优势可能更快。

实际结果需以具体硬件和测试场景为准,提议多次运行取平均值以减少误差。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
铃圆兔的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容