中科弘云面视总结

中科院支持的一家公司。主要在做功能类似TVM的产品,用在国产硬件上。主要工作内容是开发类似于cudnn/cublas这一层的库。

岗位描述:
多进程了解吗?

分布式训练有哪两种方式?

cuda的安装目录位于哪里?
/usr/local/cuda
冯诺伊曼体系结构?
1、输入数据和程序的“输入设备”;
2、记忆程序和数据的“存储器”;
3、完成数据加工处理的“运算器”;
4、控制程序执行的“控制器”;
5、输出处理结果的“输出设备”。
目前有哪两种指令集,其中精简指令集是?
RISC(精简指令集计算机)和**CISC(**复杂指令集计算机)是当前CPU的两种架构,x86是CISC的代表架构,占领了95%以上的桌面计算机和服务器市场。Arm作为RISC的一种,在智能手机、可穿戴设备等移动处理器市场占领主要地位。
计算机体系结构?
A100是什么系列?
ampere系列
显存:80G
显存带宽:1935GB/s
互联方式:NVLINK
多实例 GPU:8个
最大功耗:300瓦

SXM的全称是Symbol Extension Mode,中文意思是符号扩展模式。它是一种高带宽的 GPU 接口类型,采用了 NVLink 技术,能够实现多个 GPU 之间的高速互联。它的优点是性能高、带宽大、延迟低、维护性好,但是兼容性较差。

PCIe 是一种通用的计算机总线接口,相比 SXM 来说,传输速度会慢一些,但兼容性比较好。

FP64: 9.7 TFLOPS 是指 NVIDIA A100 这款 GPU 的双精度浮点运算性能是每秒 9.7 万亿次1。双精度浮点运算是一种数值计算的方式,它可以表示很大或很小的数,而且精度很高2。双精度浮点运算性能是衡量 GPU 在进行科学计算、仿真等高性能计算 (HPC) 应用时的能力的重要指标3。NVIDIA A100 的双精度浮点运算性能是上一代 NVIDIA Volta GPU 的 2.5 倍,是目前市场上最强大的 HPC GPU1。

FP64 Tensor core: 19.5 TFLOPS 是指 NVIDIA A100 GPU 的双精度浮点张量核心的峰值运算性能是每秒 19.5 万亿次
tensor core:用来矩阵运算加速的硬件单元
FP32:19.5 TFLOPS
Tensor Float 32 (TF32): 156 TFLOPS 是一种新的数值格式,由 NVIDIA 在 Ampere 架构的 GPU 中引入,用于加速 AI 训练和部分 HPC 应用的矩阵运算。它是一种截断的 32 位浮点数,由 1 个符号位,8 个指数位和 10 个尾数位组成,总长度为 19 位。它的优点是保持了 FP32 的动态范围,同时提高了 FP16 的计算效率,能够在不损失准确性的情况下,实现高达 10 倍的性能提升。
BFLOAT16 Tensor Core:BFLOAT 通常指的是 16 位的截断浮点数,也就是 BFLOAT16,它有 1 个符号位,8 个指数位和 7 个尾数位。它用于深度学习和高性能计算的场景中。需要专门的硬件支持。优点是节省内存空间,提高计算效率。
H100是什么系列?
Hopper系列
4090是什么系列?
NVIDIA Ada Lovelace
F100是什么系列?
CPU的指令流水线有哪些?
GPU基本组成?

CPU为什么有缓存设计?
CPU为什么有缓存设计,主要是为了解决CPU和内存之间的速度差异问题。CPU的运算速度远远高于内存的访问速度,如果每次CPU需要读写数据都要从内存中获取,那么CPU的性能就会受到很大的限制。为了缩短这种速度差距,就在CPU和内存之间加入了缓存,也称为高速缓存或CPU缓存

编译原理的过程讲一下?
编译过程主要分为五个阶段:词法分析、语法分析、语义分析及中间代码生成、代码优化、目标代码生成。这五个阶段的任务分别由五个程序完成。
词法分析器:将源程序分割成一个个最小的有意义的单元,称为词法单元或记号(token)。
语法分析器:根据给定的文法规则,将词法单元串组合成各种语法结构,如表达式、语句、函数等,并构造出相应的语法树。
语义分析器:根据语法树和语言的语义规则,进行类型检查、作用域分析、常量计算等,并生成一种中间表示形式,如三地址代码、四元式、中间语言等6。
代码优化器:对中间代码进行各种优化,如删除冗余代码、消除公共子表达式、循环优化、寄存器分配等,以提高目标代码的运行效率。
目标代码生成器:根据目标机器的指令集和寻址方式,将中间代码翻译成目标代码,如汇编代码、机器代码等。

如果让我学习TVM我会怎么学?

RISC指令集设计原则?
RISC指令集设计原则是指一种简化指令集,使得指令执行速度更快,同时减少指令的复杂度和数量的设计思想。RISC指令集设计原则主要包括以下几点:

1、单周期指令:使用频度很高(一个机器周期)的指令,如算术运算逻辑运算、数据传送等。
2、大大减少寻址方式,只保留最常用的寻址方式,如立即数寻址寄存器寻址基址寻址等。
3、固定的指令格式:采用固定长度的指令,简单的指令格式,统一的指令编码。
4、扩大通用寄存器的个数,使用寄存器而不是内存来进行操作,采用LOAD/STORE结构,只有加载和存储指令可以访问内存。
5、采用硬联控制实现,使用硬件逻辑来控制指令的执行,而不是使用微程序控制。
6、注重编译优化:通过精简指令和优化设计编译程序,以简单有效的方式支持高级语言,利用编译器的优化技术来提高程序的性能。

怎么通过一个C++程序读取cache缓存的大小?
通过一个C++程序读取cache缓存的大小的一种方法是利用程序运行时的内存访问特性,根据不同的内存块大小和访问时间来估计cache的大小。具体的步骤如下1:

创建一个足够大的连续内存块,例如一个vector或者一个数组。
对内存块进行大量的随机访问,并计时,记录每次访问的平均时间。
改变内存块的大小,重复上述步骤,得到不同大小的内存块对应的访问时间。
绘制内存块大小和访问时间的关系图,观察图中的跃升点,即可估计cache的大小。
这种方法的原理是基于程序运行时的局部性原理,即程序在一段时间内,只会访问一部分的数据和指令,而且这些数据和指令在内存中的位置也比较接近。当内存块的大小小于或等于cache的大小时,访问时间会比较稳定,因为大部分的数据都可以从cache中获取。当内存块的大小大于cache的大小时,访问时间会明显增加,因为需要从慢速的内存中加载数据。因此,通过找出访问时间的跃升点,就可以估计出cache的大小。

分布式训练了解吗?
分布式训练是深度学习中的一种技术,它允许在多个计算节点上并行训练模型,以加速训练过程和处理大型数据集。分布式训练的基本方法步骤通常包括:

环境配置:设置多个计算节点,每个节点配备所需的GPU或CPU资源,并确保节点间网络连接畅通。
修改训练脚本:调整训练脚本以支持分布式计算,这可能包括修改数据加载和模型更新的方式。
选择分布式策略:根据模型和数据的特点选择合适的分布式训练策略,如数据并行、模型并行或混合并行。
启动训练任务:在所有计算节点上启动训练任务,每个节点计算自己的梯度并贡献到全局模型更新中。
同步与通信:在训练过程中,各节点需要进行通信和同步,以保证模型参数的一致性和准确的梯度更新。
监控与调优:监控训练过程,根据需要进行调优,以提高训练效率和模型性能。
结果汇总:训练完成后,汇总各节点的训练结果,得到最终的模型1。

高性能计算了解吗?
高性能计算(High Performance Computing, HPC)是一种通过并行处理技术解决复杂问题的方法。它通常涉及以下步骤:

基础知识学习:了解计算机体系结构、操作系统、数据结构和算法,学习编程语言如C/C++、Python等。
并行计算:将问题分解为多个子问题,在多个处理器上同时执行这些子问题。
分布式计算:在多个计算节点上同时执行任务,涉及任务调度和数据传输。
高速存储:使用高速存储设备,如SSD和RAM,提高数据存取速度。
高速网络:使用高速网络技术,如InfiniBand,提高数据传输速度。
算法优化:对算法进行优化,降低计算复杂度,提高计算速度。
硬件优化:使用专门的硬件设备,如GPU和ASIC,提高计算速度。
性能监测与调优:使用性能分析工具监测程序性能,根据分析结果进行调优。
目前,有许多出色的高性能计算工作可以学习和借鉴,例如:

NVIDIA 在AI平台部门的工作,包括分布式训练、移动端推理框架开发等1。
华为 的HPC+AI融合研究,发挥鲲鹏+昇腾算力优势,进行加速库的融合技术研究2。
百度 的深度学习平台PaddlePaddle的基础框架设计和开发,以及核心技术算法在不同底层硬件上的性能优化2。
这些工作不仅展示了高性能计算在不同领域的应用,也提供了学习和实践高性能计算技术的机会。

计算机体系结构中的页交换了解吗?
页交换是一种内存管理技术,它可以将物理内存分割为固定大小的单元,称为页帧,将虚拟内存分割为与页帧相同大小的单元,称为页。当物理内存不足时,操作系统可以将一些不常用的页从物理内存中换出到外部存储设备(如磁盘)上,从而释放出物理内存空间,供其他更需要的页使用。当被换出的页再次被访问时,操作系统会将其从外部存储设备换入到物理内存中,同时可能需要换出另外一些页,以保持物理内存和外部存储设备之间的平衡12。

页交换的优点是可以有效地利用物理内存,提高内存的利用率和系统的性能,同时也可以支持多任务和虚拟内存的功能。页交换的缺点是会增加系统的开销,因为需要维护页表和页帧的映射关系,以及进行页的换入换出操作,这些操作会消耗CPU的时间和I/O的资源。为了减少页交换的开销,操作系统需要采用一些策略,如优化页表的结构,使用快表和多级页表等,以及使用合适的页面置换算法,如最近最少使用(LRU),最不经常使用(LFU),时钟(Clock)等12

虚拟内存了解吗?
虚拟内存是一种内存管理技术,它可以让电脑运行更多的程序,并缓存部分内存。虚拟内存的原理是将物理内存分割为固定大小的单元,称为页帧,将虚拟内存分割为与页帧相同大小的单元,称为页。当物理内存不足时,操作系统可以将一些不常用的页从物理内存中换出到外部存储设备(如磁盘)上,从而释放出物理内存空间,供其他更需要的页使用。当被换出的页再次被访问时,操作系统会将其从外部存储设备换入到物理内存中,同时可能需要换出另外一些页,以保持物理内存和外部存储设备之间的平衡。
虚拟内存的优点是可以有效地利用物理内存,提高内存的利用率和系统的性能,同时也可以支持多任务和虚拟内存的功能。虚拟内存的缺点是会增加系统的开销,因为需要维护页表和页帧的映射关系,以及进行页的换入换出操作,这些操作会消耗CPU的时间和I/O的资源。为了减少虚拟内存的开销,操作系统需要采用一些策略,如优化页表的结构,使用快表和多级页表等,以及使用合适的页面置换算法,如最近最少使用(LRU),最不经常使用(LFU),时钟(Clock)等12。

MLIR理解吗?
MLIR 是 Multi-Level Intermediate Representation 的缩写,中文意思是多层级中间表示。它是一种用于构建可复用和可扩展的编译器框架的解决方案,由谷歌发起并合入 LLVM 项目。它的目的是解决软件碎片化、改善异构硬件的编译、降低构建领域特定编译器的成本,以及帮助将现有的编译器连接到一起。
MLIR 的核心是一种灵活的基础设施,适用于不同的编程语言、库、框架和硬件平台。它支持定义不同的中间表示(称为 Dialect),并提供了一套通用的操作、属性、类型和转换规则,以及一些内置的 Dialect,如标准 Dialect,循环 Dialect,线性代数 Dialect 等。MLIR 还支持在不同的 Dialect 之间进行转换、优化和翻译,以生成目标代码,如 LLVM IR,SPIR-V,C++ 源码等。

笔试

一、

new、delete、malloc、free关系
new、delete、malloc、free是用于动态内存分配和释放的方法,它们有以下几点关系:
new和delete是C++的运算符,malloc和free是C/C++的标准库函数。
new和delete会调用对象的构造函数和析构函数,而malloc和free只会分配和释放内存空间。
new和delete不需要指定内存大小,而malloc和free需要指定内存大小。
new和delete在内存分配失败时会抛出异常,而malloc和free在内存分配失败时会返回NULL。
new和delete不支持内存扩容,而malloc和free可以使用realloc来扩容内存。

new 和 malloc 分配的内存都在堆上面,而不是在栈上面。堆是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。C++使用new从堆上分配内存,使用delete释放已分配的对应内存。
栈是程序运行时自动分配和释放的内存空间,用于存储局部变量、函数参数、返回地址等。栈的大小是有限的,而且不能手动控制栈的分配和释放,所以不能使用栈来进行动态内存分配。

二、简述C++中的虚函数
1、多态:虚函数允许我们通过基类指针或引用来调用派生类的实现,从而实现多态。
2、可扩展性:通过使用虚函数,我们可以轻松地添加新的派生类,而无需修改现有的基类代码。
3、代码重用:虚函数允许派生类重用和扩展基类的功能,而无需完全重写函数。

什么是死锁,它的条件是什么?怎么避免死锁?
死锁是指两个或多个进程在执行过程中,因为争夺资源或者相互等待而导致的一种阻塞的现象,如果没有外部干预,它们都无法继续运行。

死锁的产生有四个必要条件:
互斥条件:一个资源每次只能被一个进程使用,其他进程必须等待。
请求与保持条件:一个进程已经占有了一些资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时该进程会阻塞,但不会释放已占有的资源。
不可剥夺条件:一个进程已经获得的资源,在未使用完之前,不能被其他进程强行夺走,只能由该进程自己释放。
循环等待条件:存在一组进程,它们之间形成了一个环形的等待资源关系,即每个进程都在等待下一个进程占有的资源。

避免死锁的方法有以下几种3:
破坏互斥条件:将独占性的资源改造为共享性的资源,例如使用虚拟打印机代替物理打印机。
破坏请求与保持条件:要求进程在申请资源之前,必须释放已占有的所有资源,或者一次性申请所需的所有资源。
破坏不可剥夺条件:允许操作系统在必要时,暂时剥夺进程占有的资源,并在适当的时候归还给进程。
破坏循环等待条件:对所有的资源进行编号,要求进程只能按照编号的顺序申请资源,或者使用银行家算法等动态检查机制,避免进程进入不安全状态。

lock_guard guard(mt);
这个也是构造互斥锁的写法,就是会在lock_guard构造函数里加锁,在析构函数里解锁,之所以搞了这个写法,C++委员会的解释是防止使用mutex加锁解锁的时候,忘记解锁unlock了。

所以为了解决lock_guard锁的粒度过大的原因,unique_lock就出现了。

unique_lock unique(mt);
这个会在构造函数加锁,然后可以利用unique.unlock()来解锁,所以当你觉得锁的粒度太多的时候,可以利用这个来解锁,而析构的时候会判断当前锁的状态来决定是否解锁,如果当前状态已经是解锁状态了,那么就不会再次解锁,而如果当前状态是加锁状态,就会自动调用unique.unlock()来解锁。而lock_guard在析构的时候一定会解锁,也没有中途解锁的功能。
当然,方便肯定是有代价的,unique_lock内部会维护一个锁的状态,所以在效率上肯定会比lock_guard慢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值