高性能计算工程师
岗位要求:
面试问题
1.自我介绍
介绍项目和技术栈
2.CUDA GPU 加速瓶颈
分单设备和多设备的加速情况
单设备:
1.流
2.共享内存(优化 bank)
3.寄存器
4.循环展开
5.访问主存(硬件决定,128bit 或者 64bit)
6.block大小的设置(由 SM 的条件决定)
7.
多设备:
1.进程间通信方法
2.双缓冲技术(类似于流)
3.RDMA 技术
4.
3.模板计算的优化方法,如何找到瓶颈
1.双缓冲
2.主从传输方法
3.循环展开、宏定义(涉及到从核的指令缓存,不能过度展开循环)
4.修改为传输方式为跨步传输,可以减少指令?
5.检查算法减少,无效的计算
6.进程的计算任务划分,尽量减少进程间通信量
7.主核也参与部分计算
8.
子问题 神威 cpu 的特点:
主从核访存延迟
算力大小
4.RDMA 在 A 项目中加速多少?
设备不支持RDMA
5.编译过程
GCC拆分编译过程后:
预处理:gcc -E hello.c -o hello.i
编 译:gcc -S hello.i -o hello.s
汇 编:gcc -c hello.s -o hello.o
链 接:gcc hello.o -o hello
子问题:extern 一个其他文件 static 的变量或者函数,在编译过程中会发生什么?
extern 修饰全局变量,所有全局变量、函数默认带有 extern 修饰符。
static 修饰局部变量,不可被其他文件引用。函数内,变量的生命周期。
链接时候报错:无法解析的外部符号。
6.如何高效利用 L1 缓存
因为很少在做CPU多线程,就没答上。但回头想想,提高命中率、SIMD、编译器优化、代码重排确实是应该答出来的。
局部性原理:根据局部性原理,程序执行时的访存模式通常是以块为单位进行的。因此,可以通过将数据存储在连续的内存块中,以及将访问同一个内存块的代码放在一起执行,从而提高数据在L1缓存中的命中率。
数据结构:在设计数据结构时,要注意结构体成员的排列顺序,以便利用好L1缓存。可以将结构体的成员按照类型、大小、使用频率等因素进行排列,从而最大化利用L1缓存。
编译器优化:编译器可以通过指令调度、循环展开、内联函数等方式优化代码,减少对内存的访问,提高L1缓存的利用率。例如,可以将循环展开成一系列单独的指令,以减少对数组元素的访问次数。
SIMD指令:L1缓存可以利用SIMD指令进行向量化操作,加快数据处理速度。SIMD指令可以同时对多个数据进行操作,从而提高指令级并行度,减少访问内存的次数,提高L1缓存的命中率。
代码重排:在程序中,可以通过代码重排来提高L1缓存的利用率。例如,可以将代码中访问同一个内存块的语句放在一起执行,减少L1缓存的替换和预取,从而提高缓存的命中率。
程序题
1.给出最大线程总数,使用多线程加速归并排序
写出来思路没问题,需要利用线程池,但未实现。面试后实际跑起来发现是负优化,开了多线程但是CPU利用率提不上去,未解决该问题。
#include
#include
using namespace std;
class Sorter
{
public:
Sorter();
Sorter(int t_cnt);
~Sorter();
static int mergesort(int* array, int left, int right);
static int sort(int* array, int left, int mid, int right);
static int mergesort_multithread(int* array, int left, int right, int thread_cnt);
private:
int thread_cnt;
};
Sorter::Sorter(){
thread_cnt = 16;
}
Sorter::Sorter(int t_cnt){
thread_cnt = t_cnt;
}
Sorter::~Sorter(){}
int Sorter::mergesort(int* array, int left, int right){
if (left >= right - 1){
return 0;
}
int mid = (left + right) / 2;
mergesort(array, left, mid);
mergesort(array, mid, right);
sort(array, left, mid, right);
return 0;
}
int Sorter::sort(int* array, int left, int mid, int right){
int length = right - left;
int merge_size = sizeof(int) * length;
int* temp = (int*)_malloca(merge_size);
// int* temp = new int[length];
int i = left, j = mid, k = 0;
while (i < mid && j < right) {
if (array[i] < array[j]){
temp[k++] = array[i++];
}
else{
temp[k++] = array[j++];
}
}
while (i<mid){
temp[k++] = array[i++];
}
while (j<right){
temp[k++] = array[j++];
}
memcpy(&array[left], temp, merge_size);
// delete temp;
return 0;
}
int Sorter::mergesort_multithread(int* array, int left, int right,int thread_cnt){
if (left >= right - 1) {
return 0;
}
int mid = (left + right) / 2;
if (thread_cnt > 1){
thread_cnt = thread_cnt / 2;
thread t1(mergesort_multithread, array, left, mid, thread_cnt);
thread t2(mergesort_multithread, array, mid, right,thread_cnt);
t1.join();
t2.join();
sort(array, left, mid, right);
}
else{
mergesort(array, left, mid);
mergesort(array, mid, right);
sort(array, left, mid, right);
}
return 0;
}
void init_array(int* array, int length, int l = 0, int r = 1 << 10){
srand(time(0)); //设置时间种子
for (int i = 0; i < length; i++) {
int temp = rand() % (r - l + 1) + l;
array[i] = temp;//生成区间r~l的随机数
}
}
int evaluate_merge_sort(int length){
int* array = new int[length];
Sorter a;
clock_t begin, end, sum=0;
init_array(array, length);
a.mergesort(array, 0, length);
for (int t = 0; t < 10; t++){
init_array(array, length);
begin = clock();
a.mergesort(array, 0, length);
end = clock();
sum += end - begin;
}
cout << "mergesort:" << double(sum) / CLOCKS_PER_SEC * 1000 << " ms" << endl;
return 0;
}
int evaluate_merge_sort_multithread(int length){
int* array = new int[length];
Sorter a;
clock_t begin, end, sum=0;
init_array(array, length);
a.mergesort_multithread(array, 0, length, 8);
/*
for (int i = 0; i < length; i++){
cout << array[i] << " ";
}
*/
for (int t = 0; t < 10; t++){
init_array(array, length);
begin = clock();
a.mergesort_multithread(array, 0, length, 16);
end = clock();
sum += end - begin;
}
cout << "mergesort_multithread:" << double(sum) / CLOCKS_PER_SEC * 1000 << " ms" << endl;
return 0;
}
int main(){
int length = 1 << 20;
evaluate_merge_sort(length);
evaluate_merge_sort_multithread(length);
return 0;
// std::cout << "Hello World!\n";
}