# 小彭老师面试经验

最近好像很流行面经……小彭老师也来写一下。

## 泽森科工 (2021.12.07)

- 张剥士在taichi论坛里主动找上来联系，表示我们很欣赏你在太极的伟大贡献，希望和我们泽森一起创造更好的新产品(什么挖墙角)
- 张剥士在微信电话里开始语音面试了，上来就要求实现sqrt函数(牛顿迭代法即可，小彭老师不小心写错，有死循环，张剥士表示不要紧，这个你肯定现实中很容易调试改正的，主要是牛顿迭代需要导数的解析式，容易写错，naive的初学者也可以选择写二分法，余切法等)
- 有没有了解过雷神3的快速sqrt函数？(知道，但好像是神奇的二进制运算，没有深入了解过为什么这样做)
- 知道float的布局吧(23位底数mantissa，8位指数exponent，1位符号sign)
- 而牛顿迭代法需要近似，sqrt最好的近似，实际上就是把exponent位除以2，对不对？(小彭老师恍然大悟，怪不得需要右移1位，原来是让exponent除2，雷神的sqrt因为有良好的近似初值，接下来就只需要一两步就能收敛)
- 接下来脑筋急转弯：我有从 1 到 100 这 100 个数，组成超长的数组，随机排列，现在这里面缺少了一位(比如 1 2 3 4 5 7 8 9 10 就是缺少了 6)
- 现在，我一边报数你一边操作，我报完的那一刻，你必须立即告诉我，缺失的那一个数是什么，并且不允许使用记事本(也就是要求常数空间复杂度，很简单，你报的同时我往一个计数器里累加求和，然后算出来的总求和和 5050 相减，得到的就是缺失的那个数了，比如 1+2+3+4+5+7+8+9+10 - 55 = -6，那么就知道缺的是 6)
- 如果我是缺了两个数呢？(那我弄两个计数器，一个求和，一个求积，然后联立二元二次方程)
- 我提供一个生成0到1区间均匀随机数的函数frand，如果我指定一个分布，比如要求高斯分布，如何生成符合这个分布的随机数生成函数？(frand是均匀随机数函数，要映射到指定密度分布的函数，可以先对该概率密度函数pdf求积分，得到累积概率函数cdf，求其反函数icdf，然后使用icdf(frand())即为符合该分布的随机数生成函数，例如对于高斯分布来说，他的cdf函数是erf，也就是erfinv(frand())可以生成高斯分布的随机数）
- 写一个函数，生成一个圆形内均匀分布的点(初学者naive的写法：先生成一个正方形的均匀分布点，然后判定是否在半径1内，如果在则保留，否则重新生成，直到生成在半径内；小彭老师的写法：首先求圆形周长S=2pir的icdf，然后blahblah，最终是r=icdf(frand())，然后再随机一个theta=frand()*2pi，x=rcostheta，y=rsintheta)
- 假如我指定的函数不是一个解析式，而是一个离散的密度数组，怎么生成符合该分布的随机数？(首先求该密度数组的前缀和prefix sum，然后r=frand()，查找第一个大于r的下标位置，该下标即为要生成的随机数，如果有一个值列表，则用下标访问这个值列表，如果需要连续变化，可以在第一个大于等于r和第一个大于r的两个元素之间按r多出的余数插值)
- 可是遍历去查找第一个大于r的位置可能会很慢，怎么加速？(二分法搜索该离散的密度数组，可以用标准库的lower_bound)

事后

- 后来小彭老师在职期间，张剥士一顿吹捧：你是我们这里唯一一个通过硅谷级人才测试的员工，一见到新员工就吹小彭老师，你看看人家小彭老师。
- 张剥士还画出了期权大饼，讲了一系列我听不懂的虚拟期权概念金溶术语后，总之翻译一下就是鼓励小彭老师奋斗，奋斗的公司股价涨了就能分红，但直到最后都没有兑现，而且据了解上市公司才有期权……
- 小彭老师看到张剥士这么重用，以为毕业以后肯定给转正了，所以就没有考研，没有去看校招，也没有接受学校的对接，忘我地伺候zeno工作，打算一毕业就直接去深圳伺候张剥士。
- 小彭老师在 zeno 的工作基本完成了，找不到有什么需要做的，张剥士突然开始反复粗鄙语言羞辱小彭老师，pua小彭老师，曰“你是程序员钓丝思维”“根本不懂我们的实际需求”“你们钓丝程序员是最不懂美术素养的”，看在剥士还在发工资的份上，就没有理它。
- 小彭老师终于毕业了，表示可以转正，这时张剥士却出尔反尔，各种推脱，曰“资金困难”“请不动小彭老师”“等我9月拉到投资好不好”，然而至今没有反应，转头就看到朋友圈在发布外包jd。
- 小彭老师反复“哀求”张剥士“开眼”，张剥士反复推脱后，终于表示：看在你对zeno也有“感情”了，如果你还想为我们zeno继续“贡献”的话，可以开出5k低价重新雇佣小彭老师，等“拉到投资”“资金不困难”时，再给小彭老师“补贴”回原来的16k工资，同时看到张剥士在群里炫耀他船新的等身手办(射)。
- 张剥士自我感动演的惟妙惟肖，小彭老师考虑到张剥士期权大饼的前车之鉴，就没有相信剥士的“好意”。
- 小彭老师只好以本科应届生身份开始寻找工作，其中hr经常拷问“我看你1月到现在都是没有工作的状态？”“说一下离职原因”造成很大的麻烦。
- 小彭老师展示“工作经验”时，hr总是反复强调“我看你才刚毕业呀？”，认为“实习经验”不算，导致小彭老师在张剥士的经验几乎作废，而应届生优惠又被张剥士耗掉，然而，它只要看一下zeno贡献排行榜就知道，小彭老师的贡献是第一的。
- 所有泽森员工都需要熟悉小彭老师反复迭代过的节点系统，才能开始他的zeno开发，更何况小彭老师还贡献了包括节点编辑器、Python bindings、实时三维视窗、多进程通信、磁盘缓存、对象序列化、实时optix、shader节点、GLSL codegen、刚体仿真、Prim属性系统、ZFX编译器、PrimPrim 邻居查找、插件系统、ABC 加载器、几何节点、VDB 节点、流体子图、Blender 插件、OpenSubdiv 集成、libigl 集成、CI/CD 工作流等诸多功能。
- 张剥士发知乎文章爆论：我们不需要程序员！只需要偶尔招两个厉害一点的实习生，做个一两年，把软件做完以后，就不需要他们了，显然小彭老师就是这样一个一次性又特别好用的实习生。

## 某 Unity 小厂 (2024.08.12)

- 一个看起来可能是老板的人物出面迎接，进入一个独立的会议室开始面谈。
- 之前玩过哪些游戏？(主要是肉鸽和模拟经营，着重介绍了杀戮尖塔和KSP，因为他们是Unity游戏，还介绍了制作以撒的结合模组的经历，基于Lua API的)
- 面试官说现在 KSP2 没有卡顿了，因为 GC 优化了
- 问学过Unity吗？(了解一点8，之前做过KSP模组)
- 问那么你的Unity版本？(糟糕，只能装傻了，因为其实很久没打开过了，而且新电脑里也没有下Unity)
- 表示你应该多看看Unity官方文档，特别是英文的文档
- 问PBR流程？(分为Lambert和Cook-Torrance两个模型，合并起来，lambert很简单的全方向随机漫反射，介绍了NDF, GDF, FDF三个函数的物理意义)
- 问Cg着色语言？(Unity自己的着色器语言，不熟悉，表示之前写的都是GLSL，但我知道所有引擎都有一个钦定语言，在不同平台会翻译生成不同的目标语言比如GLSL，Unity也支持写GLSL，但部分功能特性API有所缺失，所以主要还是写Cg，在GL后端会自动翻译生成GLSL的)
- 小彭老师提起为了支持UE，特别做了HLSL后端(面试官锐评了煞笔DX不跨平台，现在游戏主要还是基于OpenGL在做，UE是因为微软给钱了才钦定HLSL为主语言，但实际上也有翻译到GLSL的OpenGL后端)
- 问到了PBR计算量大，移动端如何解决性能问题(小彭老师表示不熟悉，之前做的都是桌面端的渲染，只知道移动端好像是tile-based rendering的)
- 表示我们做的是Unity的微信小游戏，因为最近推出的WebGL后端(小彭老师：怪不得可以看到最近这么多微信小游戏)
- 问了 Lua(小彭老师表示熟悉，因为之前写的 Lua 脚本实现以撒模组)
- 我们不是要写 Lua，而是要调用 Lua 的 C 接口哦！(小彭老师表示也熟悉，因为之前参与的一个以撒模组项目 IsaacSocket 就是基于 C# 客户端，动态往以撒进程注入 C++ DLL，劫持以撒的各种回调，然后暴露出 Lua 接口，创建了一系列对游戏内 Lua 脚本可见的 C++ 函数，实现以撒 API 的扩展，需要 lua_tonumber 来获取参数，lua_pushnumber 来返回值等)
- 问了 lua_State 是什么(小彭老师：含有 lua 的堆栈全局变量等上下文信息，可以认为是一个线程，每个线程各自独立)
- 问如何创建一个 UI 界面？选择不同的服务器，登录(使用json+http实现rpc，获得一个列表，然后设置列表，即可利用MVC创建出界面)
- Unity 的 UI 系统会写吗？(不会，只知道Qt有QListView控件，Unity可能也有吧？)

## 蔚来小汽车 (2024.08.15)

1面（疼逊会议语音沟通）

- TODO 还在写

2面（疼逊会议语音沟通）

- TODO 还在写

没有3面了，原因不明。

## 其域科技 (2024.09.03)

1面（疼逊会议语音沟通）

- 要求自我介绍(主要介绍了泽森在渲染的工作，shader节点等，不仅负责了Qt+OpenGL实时可视化，后来还加入了OptiX实时光追支持)
- 小彭老师主动介绍zeno的shader节点支持输出为GLSL、HLSL、CUDA三种后端格式，都是codegen，其中CUDA后端采用NVPTX编译，供OptiX使用
- 介绍PBR流程(albedo, metallic, roughness 等，还有法线贴图的烘烤，IBL 光照等)
- 问到了PBR计算量大，移动端如何解决性能问题(小彭老师表示不熟悉，之前做的都是桌面端的渲染，只知道移动端好像是tile-based rendering的)
- 介绍了公司内部是有嵌入式(三维捕获设备)和电脑端(用于查看三维捕获结果的App)，嵌入式设备只需要做初步的数据处理，基于国产GPU(但是CUDA接口)，初步处理后回传用户电脑，电脑端可以假定用户是NVIDIA显卡(也是CUDA接口)用于更massive的后处理和渲染可视化
- 小彭老师主动介绍在taichi three中手搓软光栅渲染器的经历(支持ssr, ssgi, taa等)

2面（LeetCode在线面试，有共享代码编辑器）

- 要求小彭老师自我介绍(简历他已经拿到看着了，就不复读名字和学历了，所以主要介绍项目，在zeno中，小彭老师担任渲染和节点系统的开发，介绍Qt+OpenGL视窗可视化，介绍节点系统类似于低代码的优势，简要介绍taichi, nbodysolver, co_async……炫耀高效的协程，并安利小彭老师比站频道，cpp公开课)
- 面试官表示不熟cpp20，提出要以cpp17为基础(没关系，我们在zeno和taichi也都是用cpp17)
- cpp有哪四大cast(static_cast, dynamic_cast, const_cast, reinterpret_cast)
- static_cast vs dynamic_cast适用场景(子类转基类总是static_cast，一个基类指针，如果不能确定是不是子类，那就需要dynamic_cast，如果失败会返回nullptr，记得检查！如果dynamic_cast引用则失败抛出bad_cast异常)
- dynamic_cast背后RTTI原理(实际上是比较typeid是否兼容，typeid指针存在虚函数表里，只有带有至少1个虚函数的称为“多态类”的类型会生成RTTI信息，顺便推销了为什么llvm选择开启-fno-rtti和-fno-exception，是避免二进制膨胀)
- llvm关闭了RTTI，那他是如何变相实现dynamic_cast的？(我知道，是使用枚举类型！定义了getType虚函数，返回枚举，用于比较)
- 可是如果D2继承D1继承B，如何保证D2也可以cast为D1？(完了，llvm源码看的不仔细，只能用以撒的做法盲猜一个：是B有getD1和getD2两个虚函数！他们默认返回nullptr，只有D1会重写getD1，D2会重写getD2，内部都是简单的返回this)
- const_cast未定义行为(本来是const的不能去掉const后访问，展示了成员函数复用两个data的用法)
- const_cast后返回指针没问题，面试官改成返回引用，涉及解引用，问这样还安全吗？(迷惑性很强的问题，被小彭老师识破：非const地解引用const变量是未定义行为，即使没有读取，就和end迭代器不能解引用一样，即使没有读写访问)
- const vs constexpr变量区别(const可以有地址，而constexpr不一定有)
- reinterpret_cast用法，什么情况下安全(小彭老师写经典举出int和float之间bit-cast的案例，介绍值转换和按位转换的区别，勾引面试官上钩，他肯定认为小彭老师不知道reinterpret_cast不能做int和float的指针转换，于是发问：)
- 面试官写出int和float指针强转并解引用的代码，问这个安全吗？(上钩了，小彭老师即答：未定义行为，不能用reinterpret_cast转int和float指针，是因为strict-aliasing认为int和float不兼容，转换后解引用即UB，面试官说似乎只有GCC会用这个，小彭老师答：GCC默认开启，可以用-fno-strict-aliasing关闭，MSVC不利用此规则优化，小彭老师顺便介绍指针别名与优化的关系，即为什么提出strict-aliasing规则的原因)
- 小彭老师强调int和unsigned int是兼容，面试官就问为什么char可以？(reinterpret_cast有破格允许的特例，char,unsigned char,std::byte与所有类型都兼容，转为char指针后可以随意访问任意类型的内存，指出是cpp标准为了方便我们网络收发包时，解序列化的方便性)
- 如何实现真正安全的int和float转换？(使用memcpy或cpp20的bit_cast是安全的，并推销memcpy vs bit_cast区别：虽然cppref上bit_cast的“参考实现”是memcpy，但得益于其基于编译器开洞__builtin_bit_cast，让bit_cast是constexpr函数可编译期确定)
- inline关键字作用(non-odr external linkage，建议头文件中就地定义的函数都加inline，否则多个.cpp文件中产生多个定义，触发链接器odr报错，而inline则是非ndr的外部链接)
- 面试官问inline函数多个文件中看到的函数体定义不同，但函数参数列表相同，会怎么样？(未定义行为，标准要求必须保证所有.cpp翻译单元看到的inline函数定义一致，否则编译器往往并不报错)
- 小彭老师推销：这导致你在cpp文件中定义非POD类会被坑，因为类体内就地定义的成员函数，默认是inline的，包括默认构造函数，当时导致zeno调试了半天(面试官表示他之前集成一个开源库也遇到这个坑，小彭老师表示可以套匿名namespace解决)
- push_back vs emplace_back(2重载vs万能引用+变长参数，emplace可以就地带任意参数构造你的元素类型，emplace的额外好处是触发explicit的构造函数而无需显式写出类名，也带来了危险，所以我在课程中都不推荐使用，如需避免移动可以`vector<unique_ptr<T>>`，这还能使扩容时也不触发移动)
- 什么情况下不会移动？面试官似乎在试探我是否了解 vector 扩容原理(只有当size>capacity时才会触发，每次触发扩容时gcc增加到2 x size，msvc则是1.5 x size，总共2n次操作，好处是保证了总体O(n)复杂度，我们建议知道长度的情况下，可以调用reserve提前预订100的capacity，这样只有推入第101个元素才会扩容到200，小彭老师顺便推销了vector, deque, list的区别，迭代器失效原因)
- 还问了万能引用和完美转发的原理(引用折叠)
- 什么是POD(基础类型、指针、无用户构造函数的纯基础类型组成的结构体)
- 小彭老师反向提问：vector超缓存大resize导致memset性能影响？(拿出我的tbb课程的parallel_filter案例，利用pod模板，面试官表示熟悉缓存，不用提问了)
- 面试官提问PIMPL模式(写C::Impl给他看，又问PIMPL的目的是什么，一开始答：分离定义，加速编译，问还有什么作用吗？保持abi稳定，不用重新编译依赖者，可用于插件热装载)
- 小彭老师反向提问：知道为什么C的构造函数里会需要unique_ptr类型析构函数的信息吗？(面试官：因为析构函数需要知道sizeof!=0，小彭老师：但是，C的析构函数被转移到了C.cpp，为什么默认构造函数初始unique_ptr为nullptr，仍然编译出错？面试官支支吾吾，小彭老师：因为构造函数可能抛异常，导致之前初始化过的成员析构，再次向他兜售cpp异常魅力时刻)
- q指针 vs d指针(Qt实现cow和pimpl的细节，因为平时没注意看qt头文件源码，小彭老师唯一栽跟头的题，说是看到简历写的Qt就问了，并表示之前zeno里主要是用PyQt)
- 小彭老师反向提问：c++11 string为什么打破abi(因为c++98 string采用cow不符合线程安全模型常识“共享读安全”)
- make_shared vs shared_ptr new的区别(只需一次性分配，无需再new SpCounter，原理是operator new+placement new)
- 小彭老师反向提问：知道为什么shared_ptr的构造函数没有noexcept吗？(就是因为要new SpCounter)
- 是operator new还是new_allocator？(默认是new_allocator，可用allocate_shared替换掉)
- 小彭老师反向提问：知道make_shared_for_overwrite的区别吗(采用default-init，new表达式后没有了括号，可避免POD类型0初始化，面试官表示这个我都没了解过)
- CRTP(奇异递归模板模式，最初用于取代虚函数，要实现获取子类指针self，推销了cpp模板类延迟实例化成员函数的机制，其实CRTP还能用于实现原型模式和visitor模式，因为时间关系没来得及说)
- 虚函数为什么低效？虚函数如何优化(因为需要call一个指针，cpu无法预知，使用final，不用读取虚函数指针表，然后说明了vecB拆成vecD1和vecD2更高效)
- shared_from_this实现(本来要我写的，因为时间来不及写，但是小彭老师表示出过手搓shared全家桶的视频，面试官只好放心)
- 你对哪个领域感兴趣？(当然是渲染，我从小做到大，并表示性能优化这一块也尽管请教小彭老师，有cuda和simd优化客户经验)
- 还有什么问题对本公司吗？(时间不多了，就问要不要现在开始学习三维重建，或者可以试试看客户端，说岗位选择可以之后和hr沟通，并向我推销了3dgs，球鞋函数，小彭老师：好多paper呀？看了几个效果图，典中典之Ours永远是最好的)

3面（疼逊会议语音）

又介绍了zeno和taichi，问了点云相关问题，很快结束了。

4面（拉投资的合伙人亲自线下见面）

- 商场全部关闭了，星巴克还开着
- 介绍一下你自己8（又是 zeno）
- 问了上次的面试官怎么样呀（我看了推荐的 3dgs，发现是把扫描出来的点云，逐步转换为椭圆球渲染）
- 你知道，现在主流图形学都是三角形网格，那么这种点云要如何渲染呢？（点云的话可以先用 marching cube 转三角形面，zeno 的流体就是这样的）
- 但是性能不够，不能保证实时（可以用屏幕空间流体，但是效果一般，我们做电影的需要高质量的离线渲染，不太注重实时性，实时椭球的话，也可以用光追，算射线与椭球表面求交即可，不过英伟达的硬件加速只有三角形的，但是 GPU Gems 上的 BVH 加速求交代码用于椭球也是可以拿来参考的）
- 拿出一台 3D 扫描机器，说你知道我们这个测绘机器是如何定位的吗（惯性制导，里面有加速度计，求二阶积分就可以得到位置）
- 但是这样时间长会有累计误差，如何消除误差？（可以用 GPS 定位，也可以通过光学摄像头扫描的结果，确定自己的相对位置，必要时可以贴几个识别纸片在墙上方便程序检测）
- 是的，实际上我们在户外会用 GPS 定位，矿洞里就会用光学的定位方法，消除惯性制导的累计误差。
- 那么 GPS 卫星定位的原理你知道吗？（三颗 GPS 卫星发出不同相位的电磁波，因为光速有限，移动设备通过检测相位差，就知道自己距离三颗卫星的距离，然后三个距离就能唯一确定一个点）

## 雅科贝思 (2024.09.03)

- 介绍一下自己（又介绍zeno是一款CAD类的项目）
- 哈希表（介绍unordered_map基于链表法，标准库的hashint是恒等函数，absl的实现基于开放地址法更高效，java也是链表法，但链表过长会转换为红黑树等）
- 红黑树（五大规则，为什么这五个规则能保证不超过2倍深度，同时比二叉平衡树高效）
- 红黑树左旋右旋操作（右儿子替换父亲，父亲变成左儿子）
- 面试官透露：实际上红黑树就是一个4阶树，你想想看（确实，如果把红黑两层看作一层的话，那么实际上是一个4阶平衡树）
- OpenGL 渲染管线（3d顶点数据 -> vert shader (矩阵变换) -> 光栅化+插值+深度测试 -> frag shader (前向着色) -> G-buffer -> 延迟渲染 (后向着色) -> 后处理 -> 屏幕）
- 来面试的人中，你是我见过技术最好的一个，之前一个硕士，上来哈希表就支支吾吾挂掉。

事后：已录取，正在上班ing……又是做 Qt + OpenGL 的项目
