高性能计算 面经1

高性能计算工程师
岗位要求:

  1. 计算机、电子、通信专业,硕士及以上学历。
  2. 精通C++语言,具有3年及以上的linux环境下C/C++多线程和多进程开发经验, 熟练掌握Linux环境下C++网络编程技术。 具有linux环境下丰富的代码调试经验。
  3. 熟练掌握计算机系统相关原理和结构,了解常见处理器(X86、ARM等)、缓存、传输总线、存储介质的基础知识。
  4. 具有OpenMP、MPI、RDMA等高性能计算相关技术的开发经验,通过对Linux内核的优化,开发出基于CPU的高性能、低延迟抖动系统。
  5. 具有多年的cuda并行计算开发经验优先。
  6. 具有优秀的团队沟通和协作能力、责任心强,善于学习,有较强的自我驱动,具有独立分析并解决问题的能力。

面试问题
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";
}

你可能感兴趣的:(笔记,c++,算法,排序算法)