当前位置: 首页 > news >正文

第三章:测量性能_《C++性能优化指南》_notes

测量性能

      • 第三章核心重难点解析
      • 关键知识点总结
      • 第三章核心内容梳理
      • 多选题
      • 多选题答案及解析
      • 设计题
      • 设计题答案要点

第三章核心重难点解析

  1. 性能测量基础
    知识点
  • 必须通过实验测量性能,不能仅凭直觉
  • 90/10规则:90%时间运行10%的代码
  • 阿姆达尔定律:优化部分的时间占比决定整体提升
  • 正确性(Trueness)与精确性(Precision)的区别

代码示例(测量时钟分辨率):

#include <iostream>
#include <chrono>

void test_clock_resolution() {
    auto t1 = std::chrono::high_resolution_clock::now();
    auto t2 = std::chrono::high_resolution_clock::now();
    
    // 连续获取时间差的最小分辨率
    auto min_resolution = t2 - t1;
    std::cout << "Clock resolution: " 
              << min_resolution.count() << " nanoseconds\n";
}

int main() {
    test_clock_resolution();
    return 0;
}
/*
编译:g++ -std=c++11 -O3 test.cpp -o test
输出示例:
Clock resolution: 21 nanoseconds
*/
  1. 高精度计时器实现
    知识点
  • 使用C++11的实现跨平台计时
  • RAII(资源获取即初始化)模式的应用
  • 避免测量误差的实践方法

代码示例(Stopwatch类):

#include <iostream>
#include <chrono>

class Stopwatch {
    using Clock = std::chrono::high_resolution_clock;
    Clock::time_point start_;
public:
    Stopwatch() : start_(Clock::now()) {}
    
    void reset() { start_ = Clock::now(); }
    
    double elapsed() const {
        auto end = Clock::now();
        return std::chrono::duration<double>(end - start_).count();
    }
};

// 测试用例
void test_stopwatch() {
    Stopwatch sw;
    
    volatile int sum = 0;  // 防止优化
    for (int i=0; i<1000000; ++i) {
        sum += i;
    }
    
    std::cout << "Loop took " << sw.elapsed() * 1000 << " ms\n";
}

int main() {
    test_stopwatch();
    return 0;
}
/*
编译:g++ -std=c++11 -O3 test.cpp -o test
输出示例:
Loop took 2.345 ms
*/
  1. 热点代码识别
    知识点
  • 内存访问是主要瓶颈
  • 循环结构对性能的关键影响
  • 函数调用开销评估

代码示例(循环优化对比):

#include <iostream>
#include <chrono>

// 未优化版本
int sum_array_v1(int* arr, size_t len) {
    int sum = 0;
    for (size_t i=0; i<len; ++i) {
        sum += arr[i];
    }
    return sum;
}

// 优化版本:循环展开
int sum_array_v2(int* arr, size_t len) {
    int sum = 0;
    size_t i=0;
    for (; i+3<len; i+=4) {
        sum += arr[i];
        sum += arr[i+1];
        sum += arr[i+2];
        sum += arr[i+3];
    }
    for (; i<len; ++i) {
        sum += arr[i];
    }
    return sum;
}

// 性能测试函数
template<typename Func>
void benchmark(Func f, const char* name) {
    const int SIZE = 1000000;
    int* arr = new int[SIZE];
    for(int i=0; i<SIZE; ++i) arr[i] = i;
    
    Stopwatch sw;
    volatile int result = f(arr, SIZE); // 防止优化
    double time = sw.elapsed() * 1000;
    
    std::cout << name << " time: " << time << " ms\n";
    delete[] arr;
}

int main() {
    benchmark(sum_array_v1, "Version 1");
    benchmark(sum_array_v2, "Version 2");
    return 0;
}
/*
编译:g++ -std=c++11 -O3 test.cpp -o test
输出示例:
Version 1 time: 1.254 ms
Version 2 time: 0.873 ms
*/
  1. 内存访问模式分析
    知识点
  • 缓存局部性原理
  • 顺序访问 vs 随机访问
  • 数据预取的影响

代码示例(内存访问模式对比):

#include <iostream>
#include <chrono>
#include <vector>

const int SIZE = 10000;

// 顺序访问
void test_sequential() {
    std::vector<int> matrix(SIZE * SIZE);
    Stopwatch sw;
    
    for(int i=0; i<SIZE; ++i) {
        for(int j=0; j<SIZE; ++j) {
            matrix[i*SIZE + j] = i + j;
        }
    }
    
    std::cout << "Sequential: " << sw.elapsed() << " s\n";
}

// 随机访问
void test_random() {
    std::vector<int> matrix(SIZE * SIZE);
    Stopwatch sw;
    
    for(int i=0; i<SIZE; ++i) {
        for(int j=0; j<SIZE; ++j) {
            matrix[j*SIZE + i] = i + j; // 列优先访问
        }
    }
    
    std::cout << "Random: " << sw.elapsed() << " s\n";
}

int main() {
    test_sequential();
    test_random();
    return 0;
}
/*
编译:g++ -std=c++11 -O3 test.cpp -o test
输出示例:
Sequential: 0.456 s
Random: 1.892 s
*/

关键知识点总结

  1. 测量优先原则

    • 所有优化必须基于实际测量数据
    • 使用高精度计时器(如C++11的chrono)
    • 每个测试用例要包含足够大的数据量(百万级)
  2. 循环优化要点

    • 减少循环内部的计算(如提前计算循环边界)
    • 循环展开(但需注意指令缓存)
    • 避免在循环内调用虚函数
  3. 内存访问优化

    • 顺序访问比随机访问快5-10倍
    • 小数据优先使用栈内存(避免堆分配)
    • 使用内存局部性原理设计数据结构
  4. 工具链使用

    g++ -S -fverbose-asm -O3 test.cpp
    
    # 使用perf工具(Linux)
    perf stat ./test
    perf record ./test
    perf report
    

    第三章核心内容梳理

核心知识点

  1. 性能测量基本原则(必须测量、90/10规则、阿姆达尔定律)
  2. 分析器工作原理和使用场景
  3. 时间测量方法(计时器实现、分辨率与准确性)
  4. 实验设计原则(基准测试、随机性消除)
  5. 代码热点识别技术
  6. 消除测量误差的实践方法
  7. 多线程环境下的测量挑战
  8. 不同时间测量API的特性对比
  9. 性能优化策略优先级判断
  10. 统计学方法在性能分析中的应用

多选题

  1. 关于分析器的正确描述包括:
    A) 可以准确预测代码的理论时间复杂度
    B) 能够直接显示内存泄漏的位置
    C) 适用于识别I/O密集型程序的真实瓶颈
    D) 在调试版本中可能产生误导性结果
    E) 可以自动优化热点函数的汇编指令

  2. 时间测量中可能引入系统误差的情况包括:
    A) 使用RDTSC指令直接读取CPU周期计数器
    B) 在虚拟机环境中测量纳秒级操作
    C) 测量前后调用GetTickCount()的时间差
    D) 使用C++11的high_resolution_clock
    E) 测量期间操作系统进行内存分页

  3. 符合90/10规则优化原则的做法是:
    A) 对全部代码进行循环展开
    B) 优先优化占用10%时间的核心函数
    C) 使用更高效的算法替换O(n²)实现
    D) 重写所有字符串操作为C风格
    E) 针对分析器显示的热点函数进行优化

  4. 在Windows平台可能产生准确亚微秒级时间测量的方法:
    A) QueryPerformanceCounter
    B) GetSystemTimeAsFileTime
    C) clock()
    D) GetSystemTimePreciseAsFileTime
    E) timeGetTime

  5. 消除测量误差的有效方法包括:
    A) 在测量前后添加空循环
    B) 使用统计方法计算置信区间
    C) 提高测量进程的CPU优先级
    D) 减少被测函数的代码行数
    E) 增加测试迭代次数

  6. 关于阿姆达尔定律的正确应用:
    A) 优化占比30%的模块使其快2倍,整体提升23%
    B) 优化占比90%的模块使其快10倍,整体提升9倍
    C) 同时优化多个小模块效果累加
    D) 仅适用于CPU密集型任务
    E) 指导优化资源分配优先级

  7. 多线程测量需要特别注意:
    A) 使用原子操作计数器
    B) 关闭超线程技术
    C) 绑定线程到特定核心
    D) 测量包含上下文切换时间
    E) 使用线程本地存储

  8. 正确的时间测量实践包括:
    A) 在循环外获取开始时间戳
    B) 使用volatile防止编译器优化
    C) 计算多次测量的中位数
    D) 优先选择最高分辨率时钟
    E) 忽略小于1%的时间差异

  9. 可能影响代码热点的因素:
    A) 输入数据规模
    B) CPU缓存命中率
    C) 编译器优化级别
    D) 函数调用深度
    E) 变量命名风格

  10. 有效的性能测试用例设计原则:
    A) 包含边界值输入
    B) 使用真实生产数据
    C) 固定随机数种子
    D) 测量冷热代码路径
    E) 包含异常处理分支


多选题答案及解析

  1. BD
    B) 某些分析器可以检测内存泄漏
    D) 调试版本可能使用调试内存分配器
    错误项:A-不能预测理论复杂度,E-不能自动优化

  2. BE
    B) 虚拟化层引入计时偏差
    E) 内存分页导致不可控延迟
    错误项:A-直接硬件读取准确,C-适合粗粒度测量

  3. BCE
    B) 优先核心函数
    C) 算法优化
    E) 热点函数优化
    错误项:A/D属于过度优化

  4. AD
    A) QPC高精度计时
    D) Win8+精确时间API
    错误项:B/C/E精度不足

  5. BCE
    B) 统计学方法
    C) 提高优先级
    E) 增加迭代次数
    错误项:A/D无关误差消除

  6. AE
    A) 30%模块2倍→1/(0.7+0.3/2)=1.176≈17.6%提升
    E) 指导优先级判断
    错误项:B-实际提升约9倍错误

  7. AC
    A) 原子操作保证计数准确
    C) 减少核心迁移影响
    错误项:D测量值包含无关因素

  8. ABC
    A) 避免循环内开销
    B) 防止关键代码被优化
    C) 中位数抗异常值
    错误项:D不总是最佳选择

  9. ABC
    A) 数据规模影响分支预测
    B) 缓存影响显著
    C) 优化级别改变代码结构
    错误项:E无关

  10. ACD
    A) 边界值测试
    C) 保证可重复性
    D) 完整路径覆盖
    错误项:B可能含敏感数据


设计题

  1. 高精度跨平台计时器设计
#include <chrono>
#include <iostream>

class PrecisionTimer {
    using Clock = std::chrono::high_resolution_clock;
    Clock::time_point m_start;
public:
    PrecisionTimer() : m_start(Clock::now()) {}
    
    void reset() { m_start = Clock::now(); }
    
    template <typename Unit = std::chrono::milliseconds>
    auto elapsed() const {
        return std::chrono::duration_cast<Unit>(
            Clock::now() - m_start).count();
    }
};

// 测试用例
int main() {
    PrecisionTimer timer;
    volatile int sum = 0;  // 防止优化
    for(int i=0; i<1'000'000; ++i) sum += i;
    std::cout << "Elapsed: " 
              << timer.elapsed<std::chrono::nanoseconds>() 
              << " ns\n";
}
  1. 多线程测量同步优化
#include <iostream>
#include <vector>
#include <thread>
#include <atomic>
#include <chrono>

void worker(std::atomic<int>& counter) {
    auto start = std::chrono::high_resolution_clock::now();
    // 模拟工作负载
    for(int i=0; i<1000; ++i) { /* dummy work */ }
    auto end = std::chrono::high_resolution_clock::now();
    counter += (end - start).count();
}

int main() {
    constexpr int threads_num = 4;
    std::vector<std::thread> threads;
    std::atomic<int> total_time{0};
    
    for(int i=0; i<threads_num; ++i)
        threads.emplace_back(worker, std::ref(total_time));
    
    for(auto& t : threads) t.join();
    
    std::cout << "Average time: " 
              << total_time/(threads_num*1000) 
              << " ns per iteration\n";
}
  1. 内存访问模式分析测试
#include <vector>
#include <chrono>
#include <iostream>

constexpr size_t SIZE = 10'000'000;

void test_cache_effect() {
    std::vector<int> sequential(SIZE);
    std::vector<int> random(SIZE);
    
    auto fill = [](auto& vec) {
        for(size_t i=0; i<vec.size(); ++i)
            vec[i] = i;
    };
    
    fill(sequential);
    fill(random);
    
    auto time_access = [](auto& vec, const char* msg) {
        auto start = std::chrono::high_resolution_clock::now();
        volatile int sum = 0;
        for(size_t i=0; i<vec.size(); ++i)
            sum += vec[i];
        auto end = std::chrono::high_resolution_clock::now();
        std::cout << msg << ": " 
                  << (end - start).count() << " ns\n";
    };
    
    time_access(sequential, "Sequential");
    time_access(random, "Random    ");
}

int main() {
    test_cache_effect();
}
  1. 分支预测优化验证
#include <algorithm>
#include <vector>
#include <chrono>
#include <iostream>

constexpr size_t SIZE = 10'000'000;

void test_branch_prediction() {
    std::vector<int> data(SIZE);
    std::generate(data.begin(), data.end(), []{ return rand() % 100; });
    
    // 未排序测试
    auto time_unsorted = [&data] {
        volatile int count = 0;
        auto start = std::chrono::high_resolution_clock::now();
        for(int n : data)
            if(n < 50) ++count;
        auto end = std::chrono::high_resolution_clock::now();
        return end - start;
    }();
    
    // 排序后测试
    std::sort(data.begin(), data.end());
    auto time_sorted = [&data] {
        volatile int count = 0;
        auto start = std::chrono::high_resolution_clock::now();
        for(int n : data)
            if(n < 50) ++count;
        auto end = std::chrono::high_resolution_clock::now();
        return end - start;
    }();
    
    std::cout << "Unsorted: " << time_unsorted.count() << " ns\n"
              << "Sorted  : " << time_sorted.count() << " ns\n";
}

int main() {
    test_branch_prediction();
}
  1. SIMD向量化效果验证
#include <immintrin.h>
#include <chrono>
#include <iostream>

constexpr size_t SIZE = 16'000'000;

void test_simd() {
    alignas(32) float a[SIZE], b[SIZE], result[SIZE];
    
    auto fill = [](float* arr) {
        for(size_t i=0; i<SIZE; ++i)
            arr[i] = static_cast<float>(i);
    };
    
    fill(a);
    fill(b);
    
    auto time_std = [&] {
        auto start = std::chrono::high_resolution_clock::now();
        for(size_t i=0; i<SIZE; ++i)
            result[i] = a[i] + b[i];
        return std::chrono::high_resolution_clock::now() - start;
    }();
    
    auto time_simd = [&] {
        auto start = std::chrono::high_resolution_clock::now();
        for(size_t i=0; i<SIZE; i+=8) {
            __m256 va = _mm256_load_ps(&a[i]);
            __m256 vb = _mm256_load_ps(&b[i]);
            __m256 vres = _mm256_add_ps(va, vb);
            _mm256_store_ps(&result[i], vres);
        }
        return std::chrono::high_resolution_clock::now() - start;
    }();
    
    std::cout << "Standard: " << time_std.count() << " ns\n"
              << "SIMD    : " << time_simd.count() << " ns\n";
}

int main() {
    test_simd();
}

设计题答案要点

  1. 高精度计时器设计

    • 使用C++11 chrono库保证跨平台性
    • 模板化时间单位支持灵活输出
    • 通过volatile防止编译器过度优化
    • 测试用例展示纳秒级测量能力
  2. 多线程测量同步

    • 使用atomic保证计数安全
    • 分离线程创建与等待逻辑
    • 计算平均时间消除个体差异
    • 注意原子操作的性能影响
  3. 内存访问模式分析

    • 对比顺序访问和随机访问性能差异
    • 使用大内存块突显缓存效果
    • volatile防止循环被优化
    • 展示缓存对性能的显著影响
  4. 分支预测优化

    • 对比排序前后条件判断速度差异
    • 使用相同数据保证可比性
    • 展示现代CPU的分支预测能力
    • 验证排序对分支预测的帮助
  5. SIMD向量化验证

    • 使用AVX指令实现8路并行
    • 内存对齐保证最佳性能
    • 对比标量计算与向量化速度差异
    • 展示SIMD的潜在加速比

相关文章:

  • 可以把后端的api理解为一个目录地址,但并不准确
  • MQTT协议笔记
  • AI之山,鸿蒙之水,画一幅未来之家
  • 网络之数据链路层
  • 【赵渝强老师】Oracle数据库的客户端工具
  • python爬虫WASM
  • 大模型工作原理深度解剖:从Transformer架构到知识涌现的范式革命
  • 全息教学系统的软件开发,沉浸式数字沙盘展示系统如何改变历史教学
  • 蓝桥与力扣刷题(蓝桥 蓝桥骑士)
  • 腾讯滑块验证码自动分析工具:原理与实现
  • 《TypeScript 7天速成系列》第3天:TypeScript高级类型通关秘籍:泛型+联合+交叉类型实战
  • 【react】类组件和函数组件的区别
  • Xshell能连接远程服务器,vscode连接远程服务器一直让输入密码,连不上
  • 炫酷的3D卡片翻转画廊实现教程
  • b站视频提取mp4方案
  • Vue3中keep-alive缓存组件应用场景。
  • HarmonyOS Next~鸿蒙系统开发类Kit深度解析与应用实践
  • C++11QT复习 (四)
  • Linux上位机开发实践(积极使用SoC厂家的SDK)
  • Linux系统perf命令使用介绍,如何用此命令进行程序热点诊断和性能优化
  • 葡萄牙、西班牙突发大范围停电,交通和通信服务受到严重影响
  • 连演三场,歌剧《义勇军进行曲》在上海西岸大剧院上演
  • 日本大米价格连续16周上涨,再创最高纪录
  • 中消协发布“五一”消费提示:践行“光盘行动”,抵制餐饮浪费
  • 没有雷军的车展:老外扎堆,萌车、机器狗谁更抢镜?| 湃客Talk
  • 商务部:将打造一批国际消费集聚区和入境消费友好商圈