面试题答案
一键面试整体设计思路
- 任务划分:
- 采用分治法,将
std::vector<int>
划分成多个子向量,每个子向量由一个独立的线程负责排序。例如,可以按照 CPU 核心数将向量大致均分成相应数量的子向量。
- 采用分治法,将
- 使用 STL 组件:
- 函数对象:使用
std::less<int>
作为比较函数对象,它是 STL 提供的用于比较整数大小的默认函数对象。对于自定义类型,可自行定义符合函数对象要求的比较类。 - 算法库:利用
std::sort
对每个子向量进行排序。std::sort
是 STL 中高效的排序算法,在多核环境下虽然本身并非直接并行执行,但可以通过将数据划分后在不同线程中并行调用std::sort
来实现多核利用。 - 线程相关:使用 C++ 标准库中的
<thread>
头文件创建和管理线程。例如,创建一个线程数组,每个线程负责对一个子向量进行排序。 - 同步机制:使用
<mutex>
和<condition_variable>
来处理线程间的同步问题。例如,当所有子向量排序完成后,需要一种机制来合并这些子向量,此时可以使用互斥锁来保护共享资源(如合并的结果向量),条件变量用于线程间的通信,确保所有线程完成排序后再进行合并操作。
- 函数对象:使用
- 合并子向量:
- 当所有子向量排序完成后,使用
std::merge
函数将这些有序的子向量合并成一个完整的有序向量。std::merge
是 STL 提供的用于合并两个有序序列的算法。
- 当所有子向量排序完成后,使用
性能测试和调优
- 性能测试:
- 使用
<chrono>
头文件来记录排序前后的时间点,计算排序所花费的总时间。例如:
- 使用
#include <chrono>
auto start = std::chrono::high_resolution_clock::now();
// 执行排序操作
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "排序耗时: " << duration << " 毫秒" << std::endl;
- 多次运行测试,取平均时间以获得更准确的性能数据,减少偶然因素的影响。
2. 调优: - 任务粒度调整:尝试不同的子向量划分方式,比如改变子向量的大小,找到最优的任务粒度。如果子向量过大,可能导致线程间负载不均衡;子向量过小,则线程创建和同步的开销可能过大。 - 同步优化:分析同步机制带来的性能损耗,尽量减少不必要的锁竞争。例如,可以采用更细粒度的锁,或者使用无锁数据结构来提高并发性能。 - 硬件特性利用:了解 CPU 的缓存结构等硬件特性,尽量让数据访问模式与硬件特性相匹配。例如,使每个线程处理的数据尽量在其对应的 CPU 缓存中,减少缓存缺失带来的性能损失。