01-面试讲解
# 图像处理面试讲解
# 技术点图谱
整个知识点主要分为 4 大部分:
- WebAssembly
- Web Worker
- Canvas
- requestIdleCallback
# 难点描述
模拟问题:我看到你这个项目里面,有一个亮点是使用 WebAssembly 提升图像处理性能,你把这个点展开说一说呢?当时具体遇到了什么样的问题,然后你是怎么来处理这个问题的。
问题分析
回答的时候分为这么几个步骤:
- 具体遇到了什么问题
- 你拿到这个问题的时候你是怎么思考的?
- 你最终的落地方案是怎样的?
- 落地方案的具体效果是怎样的?
参考答案
当时我们这个项目涉及到了在线的图像处理,例如对图像添加滤镜、图片变换、图片压缩这些操作。
这些功能不是简单的 CSS 就能搞定的。比如滤镜,虽然 CSS 也有,但是 CSS 的滤镜很简单,而在我们项目里面的滤镜涉及到高斯模糊、锐化、浮雕等效果,这就涉及到需要专业的图像处理库,因为图像处理库提供了专业的处理以及算法。
但是使用图像处理库进行这些复杂操作时,经常会遇到卡顿、耗时的问题。因为图像处理算法通常需要大量的数学运算,这是一种计算密集型任务,而 JS 是单线程,主线程做这种计算密集型任务就会阻塞其他操作。(阐述遇到的问题以及为什么存在这个问题)
我先考虑要不由后端来搞定这个问题,但是思前想后,后端倒是能够处理,但是需要将处理的结果及时的反馈到前端,这就涉及到网络通信,这就又是一个不确定的因素。假设用户网络环境不好,可能比优化前体验还要差。所以讨论下来还是决定前端来搞定这个事情。(拿到这个问题之后是如何思考的)
我当时首先想到了 WebAssembly,因为它是字节码格式,性能上面很有优势。在此基础上我还应用了 Web Worker、OffscreenCanvas 以及 requestIdleCallback 来做进一步优化,最终落地下来的这一套解决方案(钩子🪝),相比优化之前性能确实提升了很多。(思考出来的解决方案)
我们使用了性能测试工具 Lighthouse 和 Chrome DevTools 的 Performance 面板来进行性能测试。优化前,处理一张 4K 分辨率的图片,之前的一个操作(例如添加滤镜)平均需要 2.5 秒,而优化后同样的处理仅需 500 毫秒左右,性能提升了约 80%。(落地的方案实际的效果)
就看面试老师您这边需不需要我把这套方案展开来讲一讲。(钩子🪝)
# 技术点叙述
# 1.WebAssembly
模拟问题:那你把你的这套方案,展开来说一说。
问题分析
根据难点中用到的技术点,挨着挨着展开来讲。注意:每一个技术点讲完后和面试官互动一下。
参考答案
那我先说一下项目中针对 WebAssembly 这一块儿的处理吧。
我之前对 WebAssembly 有一些了解,这是 W3C 推出的一种低级字节码格式,可以由其他语言编译而成,例如 C/C++、Rust 这些语言,因此在性能上面就有了很大的发挥空间。
当时这个项目,本来就是要处理在线图片处理时的性能问题,所以我想到了它,这种技术有一些天然的优势:
- 高性能:因为是字节码,接近于机器码,所以特别适合执行计算密集型任务
- 语言无关:多种高级语言可以编译为 WebAssembly
(简单介绍WebAssembly以及为什么要选择它)
我先使用了 Emscripten,这是一个内置了 LLVM 工具链的编译器,可以将 C++ 代码编译成 WebAssembly. 通过这个工具,我将一个现有的 C++ 图像处理库 OpenCV 编译成 Wasm 模块,编译后还会生成一个 JS 胶水文件,之后就可以在 JS 中动态的去加载编译生成的 Wasm 模块。(阐述我的项目中是如何使用这项技术)
应用了 WebAssembly 后,项目中进行图像相关操作时性能确实有很大的提升。不过我认为这个项目里面还有进一步的优化空间,我仔细考虑了一下,想到了用 Web Worker 来做进一步的优化,Web Worker 这一块儿要继续讲吗?(钩子🪝)
# 2. Web Worker
模拟问题:可以,你继续讲吧,说一下你是如何用 Web Worker 做进一步优化的?
问题分析
- 简单说一下什么是 Web Worker
- 结合你的项目说一下你是怎么用的
- 设置下一个钩子
参考答案
Web Worker 我很早就有了解,之前有两个项目还用到过。这种技术的特点是能够创建一个独立于主线程的 JS 线程,我们就可以将一些耗时的任务放到这个新的线程里面执行。(简单阐述什么是Web Worker)所以我当时就将图形处理的任务交给了 Worker ,由 Worker 加载 WebAssembly 模块来处理图像,处理后的图像数据传回主线程并更新 Canvas.(结合自己的项目介绍是如何使用的)
结果好巧不巧,当时在使用 Canvas 的时候,一个同事问我有没有使用 OffscreenCanvas,我一下子愣住了,因为我之前不太了解 OffscreenCanvas. 于是我下来查了一下什么是 OffscreenCanvas,发现在我这个项目场景下,也是适用的,我就将 Canvas 这一块儿优化为了 OffscreenCanvas. (钩子🪝)
意外的又发现了一个可以优化的点。
# 3. Canvas
模拟问题:那你说一下 OffscreenCanvas 呢,它和普通的 Canvas 有什么区别?
问题分析
- 简单介绍 OffscreenCanvas,将它和 Canvas 做一个简单的对比
- 结合你的项目说一下你是怎么用的
- 设置下一个钩子
参考答案
OffscreenCanvas 是 Canvas 的一种扩展,允许在主线程之外的工作线程中进行绘图操作。(简单介绍 OffscreenCanvas)
前面我的项目没有使用 OffscreenCanvas,当时整体的步骤是:
- 主线程需要将图像数据传递给 Worker 来处理。
- Worker 处理完后,需要将处理后数据返回给主线程。
- 主线程再将收到的图像数据绘制到 Canvas 上。
后面改成了 OffscreenCanvas,Worker 可以直接在 OffscreenCanvas 上进行图像处理和绘制,不需要将处理后的图像数据传回主线程,并且 OffscreenCanvas 里面绘制的内容会自动更新主线程中的显示。(介绍你的项目中是如何使用的)
这也算是通过这个项目我所学到的一个新的知识。果然经验还是得靠做项目才能一点一点积累起来,而且这些经验是实实在在的,印象深刻的,不像背八股文,背完了两天不用就忘了。(钩子🪝)
比如做完这个项目后,我就对 OffscreenCanvas 印象深刻,至少短期内是不会忘记的。
另外也是通过这个项目,对 requestIdleCallback 的理解也比之前要更好一些了。以前只是背八股文,总是容易记错记混,但是这次项目里面真真实实的用到了 requestIdleCallback,现在我对这个 API 的理解也比以前更加深刻了。
# 4. requestIdleCallback
模拟问题:那你说一下你项目中如何用到 requestIdleCallback 的呢?
问题分析
还是按照上面的回答步骤走,不过这是最后一个问题,可以简单对这个项目亮点做一个概括,突出自己做事儿风格。
参考答案
requestIdleCallback 是一个浏览器 API,允许开发者在浏览器空闲时间执行后台或低优先级任务。(简单介绍这个技术)
为什么我会想着使用 requestIdleCallback 呢?
因为即使图像处理是在 Worker 中执行,但主线程仍需要与 Worker 通信、传递数据。通过 requestIdleCallback,就可以确保这些任务是在浏览器空闲时进行的,减少对主线程的干扰,主线程应该优先处理来自用户的滚动呀、点击之类的效果,给予及时反馈。
当然,可能这个项目中使用 requestIdleCallback 优化得并不太多,但是我这个人做事儿就是喜欢尽自己一切可能,做到自己能力范围内的最好。(突出你这个人做事儿的一个态度)
现在回想起来,为了解决图像处理的性能问题,考虑了各种方案,最终落地到现在的这个解决方案,并且一点一点进行优化。整套落地方案对比优化前的效果,确实性能提升很多。(对整个项目亮点做了一个总结)
不过这里面肯定也存在一些我没有考虑完善的点,不知道面试官您这边针对这个问题是如何考虑的,还有没有能够优化的地方?
(完美结尾,80%的面试官估计已经听懵了,然后故作镇定的告诉你:这个方案挺好的...)
-EOF-