字节 编译构建组¶
约 3262 个字 预计阅读时间 11 分钟
实习¶
3.2 投的,岗位是「编译与性能优化实习生」。
4.6 一面¶
自我介绍。
让仔细讲一下编译器相关的项目。问了中间代码是如何定义的。
问了栈和堆上变量的区别。
问对垃圾回收是否了解。回答了编译原理前瞻里讲的那一套(可达性、类型安全)。问了这些理解是从哪里学来的。回答了朋辈辅学备课的时候看到的。问了编译器需要对垃圾回收做什么支持。我分析了是否使用垃圾回收的优劣,提出可以都实现,但是用编译选项来让用户选择。
问对 LLVM 了解吗,回答不了解。
问代码到运行需要哪些过程。问了动态链接的机制是怎么样的。回答并没有相关了解。问了动态链接和静态链接的区别是什么。我也不太清楚。
问对 ELF 格式了解吗,回答不太了解。
问有没有用过 gdb,回答没太用过。问写代码怎么调试的,回答都用 IDE 调试的。问了断点是怎么实现的,我不太清楚。问断点的时候能够看到调用栈,这个是怎么实现的。我也不太清楚。问调试的时候能看到各个变量的值,是怎么实现的。我还是不清楚。啥都不会🤪只能猜一猜,猜的也都不太对
问函数调用的时候编译器要生成什么代码。就是汇编函数调用的那一套。
让介绍一下解释器的细节。问了如果想捕获引用怎么做。
算法问题。有 0 ~ N-1 这 N 个点,有 N-1 条有权无向边,这 N 个点是互相连通的。问题是图上最长的路径是多长。其实就是 树的直径,可惜我不会。能想到的一个做法是任选一个点 A,然后找到由它出发最长和次长的两条路径,终点分别为 B 和 C;对于这三个点中的每两个 X Y,计算它们的最近公共祖先 L,三组 X, Y 中对应的 X-L-Y 的长度中的最大值是答案。
进一步,回答从每个点出发的最长路径是多少。想了一会儿不会,于是结束了。
反问阶段。问了有什么改进的建议,回答整体还可以,保持学习就行。问了去了以后会做什么,回答有 JVM,C++ 相关的优化、分析业务代码找优化点、新语言和工具的探索之类的内容。具体还要看分到哪个组。
4.7 二面¶
自我介绍。
问了解释器和编译器的区别。我从概念和实现上做了区分,并提到解释型语言能完成「运行一个字符串」这样的操作,但是编译型语言很难。
追问了解释型语言相对编译型语言的优点,我回答了可移植性,网页里要放代码更适合解释型语言。
追问了缺点。我回答了效率问题。他问我知不知道 Java 是怎么运行的,我说大概知道,就是编译成字节码然后在 JVM 里解释执行,解释了 JIT 机制。他问解释型语言还有什么缺点,提示了「如果一次运行正确,这个代码就没问题了吗」,于是我解释了不是这样的,因此错误检查和测试是个问题。
他指出 Java 在 JVM 里解释执行的时候是能拿到上下文的。追问了解释型语言性能方面还有什么优点,我猜测如果能拿到上下文,那就可以在运行过程中更好地预测分支,从而做对应的优化。
他追问,既然解释型语言有这个优点,那编译型语言如何能改善这个问题呢?我没猜出来,他提示,如何让 C 和 C++ 的编译器能知道运行时的信息呢?我没想到。他提示,可以先走一遍把信息采集到,下一次运行再利用这个信息。他问如何实现这种统计呢?我猜可以打 log。他说这个很繁琐,怎么解决呢?我又没猜出来,他说可以用一些插桩,把统计数据写一个 profile 文件。这个叫 Profile-guided optimization (PGO)。
问了这个东西有什么缺点。回答了自己的测试和实际情况不一定相符。
他问对编译器本身的优化有多少了解。我说基本没什么。问了编译器项目主要做了哪些事情。
问如果有一个 C++ class,在全局分配了一个对象,运行的时候它要在 main 之前被构造。这是如何实现的?我猜就是编译期生成一个函数,存放所有需要初始化的静态对象,然后在程序入口和 main 之间调用这个函数。他说这种方式对于多文件来说是不行的。我猜在链接的时候可以把各个 .o 的列表合并起来。他问我列表里要存什么,我说要存每个需要初始化的对象指针和对应构造函数的指针。他说我的方法没问题,但是只需要存一个指针就够了,问我为什么。我没说出来,他解释说链接时是有符号表的,符号表是知道每个对象是哪个类的。
他问如果有若干全局对象,谁会被先初始化。我回答应该是链接的时候决定的,但是我不理解这个顺序会有什么影响。他追问链接时这个顺序取决于什么。我没答出来,他说取决于前面那个表里的位置。我问他表里的位置怎么决定的,他说是链接器自己实现的,也就是说是个 UB,因此这里是可能出问题的。
他问假设希望这个顺序确定下来有什么办法。我说如果我来实现,我会在 main 函数里面手动构造这些对象。他说这样不太合适,只能放在全局。他提示可以给链接器提供信息规定谁在前谁在后,问我怎么提供信息。我没答出来。他提示链接器能看到的是哪些信息,我说是函数符号。他说那只能在符号上做文章。我没想出来,他说规定好符号的格式,然后去改链接器😕他也不早说能改链接器
他介绍实际的方案是用 attribute,给一个权重。编译器按照这个权重生成符号的时候加信息,链接器根据信息处理链接。
问 C++ 里 A *p = nullptr;
,A
里有一个 foo()
,问 p->foo()
的结果是什么。回答了如果是 static 的话没什么关系;如果不是 static 的话 p
会作为 this
被传进去,如果过程中使用了 this
就会段错误,没使用的话就没问题。
问一个算法题。有一块 buffer,大小是 N byte,问里面有多少个 bit 是 1。我回答循环右移,然后每次 &1。他问有没有好一点的做法。我说每次用若干字节查表。
反问阶段。问了哪些方面需要加强,他说编译器的工程难点都不在前端,更多要往中后段的优化走;我对优化的理解还比较少。前面说的那些问题看一看 IR 就知道怎么回事了。
4.15 三面¶
这场只有不到 30 min。是和头头聊的。
他介绍团队核心目前不在编译器本身,而是在编译器的应用,即有哪些应用、哪些陷阱,有哪些不足的地方。只有少部分一些精力投入在新方向,例如用 LLVM 做 golang 的后端。做一个编译器产品本身并不是团队目前核心的工作。他介绍第一位面试官对编译器是很熟悉的,现在在用 LLVM 做 golang 的后端;第二个面试官主要是做性能优化的。也有看新的硬件架构和编译器的适配。
他问我有什么问题。我问「编译器的应用」主要是有什么例子。他说公司发展比较快,很多同事是从其他语言转过来的,因此对于一些语法特性的使用不是很熟练,因此业务里面可能会出现一些性能问题:例如有的时候该 catch 的异常没有 catch,就会再抛一层,这个就会带来性能的损失。所以可能做的一些任务是解决代码质量的问题。
他让我讲一下什么时候会出现 page fault。当时还是不太会哇——只说了交换的可能。其实有 4 种可能的。
问有没有看过 CSAPP 以及关于 CPU 微架构的内容。问我知不知道 TLB,我当时不记得了,所以说没印象。
问有没有读研的打算。我说能保就读,不能就就业。
他介绍了组里的各种工作。讲了各种方向,同时也提到了可能很多时间放在业务的优化里。
我问去了之后有没有机会真正做一个项目出来。他说可能不是很确定,很难说在短短几个月做出一个 project。
4.18 HR 面¶
大概 20 min。
问了后续打算直接工作还是读研。我说能保就读,不能就就业。
问对不同课程是如何划分经历分布的。我说通识课有正态而且不太愿意去卷,所以成绩低一点;专业课就会花更多经历。
问对 base 上海还是杭州有什么倾向。我说组一样的话倾向杭州。
问对 mentor 的预期。我说希望有更具体的阶段性的指引。
他问如果 mentor 给了难度很大的任务,我怎么处理。问了给的任务难度比较低或者东西很少,我怎么处理。
问我过往有什么比较有成就感的项目。有哪些收获和成长。
问觉得自己有什么优点和缺点。
问了还有哪些正在面试中的公司。介绍了投了华为,但是华为在养鱼,比较失望,所以来投了字节。
问了对实习有什么期望。我说希望搞真正和业界相关的东西。
问了实习的时间。
4.20 接到了 offer。
秋招¶
9.22 一面¶
大概 40 分钟左右。
之前聊过,离现在也没多久,所以这次不问技术方面的问题了。
问上次有 offer 之后为什么没来。除了跟蚂蚁说的两个原因之外,还说了实习三面的时候面试官介绍工作比较零碎,很难有完整的项目经历。
问了在哪里实习的,具体工作有哪些。介绍了一项工作的细节。
问我对编译器相关的工作的理解是什么样的、我认为编译器相关的工作在工业界有什么应用。
问我针对具体业务的优化可能和编译器本身已经关系不大了,我如何看待这种工作。(所以这个公司真的没有 code review 吗 😢
问我对自己的职业发展有什么规划。回答没什么规划,因为没什么了解。
问我擅长做什么工作、喜欢做什么工作。为什么在这点比别人擅长。
反问阶段。问了具体的工作内容。问了对员工的期待是什么。回答说比较在意积极性。
9.27 二面¶
自我介绍。介绍了前面那项工作的细节。
问我如果有一个系统需要通过汇编等方式实现一个原子操作,有什么思路。我说我只知道一些架构支持原子性的操作,比如 RISC-V。
问我如何来介绍 RISC-V。我聊到了精简指令集和指令等长。他问我了不了解 S-mode, M-mode 之类的,我说不了解。
写代码环节。给出一个链表,做一个修改。例子是:
Input : 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
Output: 0 -> 9 -> 1 -> 8 -> 2 -> 7 -> 3 -> 6 -> 4 -> 5
Input : 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
Output: 0 -> 8 -> 1 -> 7 -> 2 -> 6 -> 3 -> 5 -> 4
如果我们写的一个程序可能会运行在不同架构或者平台下,希望程序自己判断运行在什么架构上,或者是否在虚拟化的环境里,有什么方法可以做到这样的事情。
问的问题都是我不会的,很烦,没意思。没有反问环节。
9.28 收到邮件说挂了。