2022 年 3 月 29 日 — LinkedIn 的客座文章Mark Pascual,高级软件工程师 Nitin Pasumarthy,软件工程师 简介 LinkedIn 的性能团队优化了网页和移动页面加载的延迟。更快的网站可以提高客户参与度,最终提高 LinkedIn 的收入。这个概念已被许多其他公司也很好地记录下来,他们也经历过类似的经历,但如何定义页面加载时间和参与度之间的最佳权衡?
LinkedIn 的客座文章
Mark Pascual,高级软件工程师
Nitin Pasumarthy,软件工程师
LinkedIn 的性能团队优化了网页和移动页面加载的延迟。更快的网站可以提高客户参与度,最终提高 LinkedIn 的收入。这个概念已被许多其他公司也很好地记录下来,他们也经历过类似的经历,但如何定义页面加载时间和参与度之间的最佳权衡?
速度和参与度之间的关系是非线性的。快速加载的网站,在一定程度上,可能不会通过进一步减少加载时间来增加参与度。在 LinkedIn,我们利用了参与度和速度之间的这种关系来有选择地定制LinkedIn Lite 上的功能 - 一个更轻、更快的 LinkedIn 版本,专为移动网络浏览器构建。
为此,我们训练了一个深度神经网络来实时识别对 LinkedIn 的请求是否会导致快速页面加载。根据此模型预测的性能质量结果,我们在向客户端发送生成的网页之前,更改了给定用户新闻提要中所有图片的分辨率。这导致 Feed 病毒式行为的规模增加了数十亿次(+0.23%),参与 Feed 的用户数增加了数百万(+0.16%),赞助收入也大幅增长(+0.76%)。
图片质量对比:左侧图片使用的内存是右侧图片的 4 倍,对于在慢速网络连接或设备资源不足的情况下向用户发送来说不太理想。在使用机器学习模型之前,我们只显示低分辨率图片,这对那些在新设备上拥有更高质量图片容量的用户来说不是很好。 |
我们在 2017 年详细描述了为什么我们许多性能优化实验失败,以及我们如何利用这些经验教训在我们个性化性能:实时调整应用程序以适应会员环境博客中构建性能质量模型 (PQM)。
PQM 的大胆目标是使用最终用户的设备和网络特征预测任何网页/移动页面的各种性能指标(例如页面加载时间),从而使(Web)开发人员能够构建具有影响力的应用程序功能,否则这些功能很难实现(就像我们上面描述的那样)。
在本文的其余部分,我们将讨论我们的全栈开发人员团队如何在生产中部署这个 PQM,该模型能够在 LinkedIn 规模上运行!我们希望证明,今天部署 TensorFlow.js 机器学习模型对于那些在 Node.js 堆栈上工作的人来说既容易又益处多多。
在我们生产部署时,LinkedIn 的 TensorFlow 模型部署机制仍在开发中。此外,使用 TensorFlow Serving 对我们来说还不可行。因此,即使我们有一个可以使用的模型,我们也需要找到一种方法来部署它。
由于 LinkedIn 主要是一个用于提供外部流量的 Java/JVM 堆栈,因此 TensorFlow Java 似乎是理想的选择,但它仍然处于实验阶段,没有我们需要的 API 稳定性保证。
我们研究了我们的选择,并意识到我们已经在我们的前端 Web 服务器堆栈中(位于 JVM 背后)使用 Node.js,以便在提供 HTML 页面时执行服务器端优化。该架构的独特之处在于我们使用 JVM 管理外部的 Node.js 进程池来执行“工作”,例如前面提到的服务器端优化。“工作”实际上可以是 Node.js 可以执行的任何事情。在我们的用例中,这使我们能够在一个已经得到验证的架构中使用 TensorFlow.js。
我们重新利用了我们的前端堆栈以使用 Node.js 来部署我们的自定义模型,最终取得了良好的结果。就性能而言,我们混合的 Java 和 Node.js 堆栈轻松满足了我们的 SLA。如表所示,从客户端(数据中心内)、主机上的工具和仅使用 TensorFlow.js 在 Node.js 中执行推理的角度来衡量的生产延迟的第 50 个百分位数和第 90 个百分位数。
第 50 个百分位数 |
第 90 个百分位数 |
|
来自客户端(数据中心内) |
10 毫秒 |
12 毫秒 |
主机上 |
8 毫秒 |
10 毫秒 |
在 Node.js 中进行推理 |
2 毫秒 |
3 毫秒 |
生成的架构如上图 1 所示。
需要预测的 API 请求由 JVM 服务器接收,并被路由到我们的Rest.li 基础设施,该基础设施又将请求路由到我们的性能预测资源。为了处理请求,PaaS 资源根据输入执行一些特征生成,然后向 Node.js 进程发出 RPC 调用以进行预测。
N 个 Node.js 进程是长寿命的。它们在 JVM 启动时启动,并已经使用 tf.node.loadSavedModel()
加载了所需的模型。当一个进程收到预测请求时,它只需获取输入特征,调用 tf_model.predict()
,并返回结果。以下是 Node.js 代码的简化版本
const tf = require(‘@tensorflow/tfjs-node’);
async function main() {
// load the model when the process starts so it’s always ready
const model = await tf.node.loadSavedModel(‘model_dir’);
function predict(rawInput) {
return tf.tidy(() => {
// prepare the inputs as tensor arrays
const x = {}
for (const feature of Object.keys(predictionInput)) {
x[feature] = tf.tensor([input[feature]], [1, 1]);
}
const output = model.predict(x, {});
const probs = Array.from(output.probabilities.dataSync());
const classes = Array.from(output.all_class_ids.dataSync());
const result = Object.fromEntries(classes.map((classId, i) => [classId, probs[i]]));
return result; // {0: 0.8, 1: 0.15, 2: 0.05} probability of each performance quality
});
}
// Register our ‘predict’ RPC handler (pseudo-code)
// process is an abstraction of the Node.js side of the communication channel
// with the JVM
process.registerHandler(‘predict’, input => {
const result = predict(input);
return Promise.resolve(result);
});
}
main();
Express 可以取代 Rest.li 的作用,特征生成部分需要移植到 Node.js,但其他一切保持不变。如您所见,该架构更加简洁,并且需要更少的脑力来管理 Java 和 Node.js 在同一个堆栈中。
在我们上面描述的架构中,外部进程不必是 Node.js。我们用来管理外部进程的库在大多数技术中都很容易实现。实际上,我们可以选择 Python 作为外部进程,因为它在这方面很流行。那么,为什么我们坚持使用 Node.js 呢?有两个原因:(1)我们已经有一个用于外部进程基础设施的 Node.js 实现,并且需要为 Python 开发一个新的实现,以及(2)事实证明,由于 JavaScript 的 JIT 编译器有利于预处理/后处理,因此 Node.js 在进行预测方面也略微快一些。
为了证明这一点,我们从现实世界的预测输入中获取了样本(约 10 万个),并在 Node.js 和 Python 上运行它们。测试台不完全是我们生产堆栈(我们没有包含 JVM 方面),但它足够接近我们的目的。结果如下所示
堆栈 |
第 50 个百分位数 |
增量(与 Python 相比) |
Python |
1.872 毫秒 |
0% |
Node.js |
1.713 毫秒 |
-8.47% |
结果表明,对于我们的特定模型,Node.js 在执行推理方面快了近 10%。当然,性能可能会根据模型架构和 Node 中执行的预处理和后处理量而异。这些结果来自我们的模型在典型生产机器上的运行结果。由于模型复杂性、机器差异等,结果也可能会有所不同。
查看我们开源仓库中的自述文件,了解我们如何在 Python 和 Node.js 中测试模型。
我们当前的独特架构确实有一些改进的空间。可能最大的机会是解决这种多堆栈架构本身的独特性。Java 和 Node.js 技术的混合在设计、开发、调试、运维、维护方面增加了额外的认知开销和复杂性 - 但是如前所述,您可以将整个堆栈移到 Node 中以简化问题,因此这是一个可解决的问题。
另一个潜在的改进领域来自目前在 Node.js 方面使用单线程架构。因此,目前一次只能执行一个预测,因此延迟有时会包含一定量的排队时间。这可以通过使用 Node 工作线程 来解决,该线程可以并行执行,这可能会在该实现的未来版本中考虑。然而,在我们的特定情况下,即使按照现在的状态,预测也非常快,因此我们目前不需要探索它。
TensorFlow.js 的可用性为我们提供了一个简单的选项来部署我们的模型以服务生产用例,而其他选项当时并不完全适合或可用。虽然我们独特的需求导致了使用非标准架构(JVM 和 Node.js 的混合),但 TensorFlow.js 可以更有效地用于同构 Node.js 服务器堆栈,从而产生非常干净且高效的架构。通过我们的开源性能质量模型,全栈 JS 工程师可以个性化性能并提高用户参与度,我们期待看到其他人如何使用我们的开源模型在其自己的网站上实现这一点。
如果没有 Prasanna Vijayanathan 和 Niranjan Sharma 的巨大努力,这个成功是不可能的。非常感谢 Ritesh Maheshwari 和 Rahul Kumar 的支持和鼓励。特别感谢 Ping Yu(谷歌)和 Jason Mayes(谷歌)的持续支持和反馈。
2022 年 3 月 29 日 — LinkedIn 客座文章Mark Pascual,高级软件工程师 Nitin Pasumarthy,软件工程师 介绍 LinkedIn 的性能团队致力于优化网页和移动页面的加载延迟。更快的网站可以提高客户参与度,最终为 LinkedIn 带来收入。这个概念已被许多其他公司所证实,他们也有类似的经历,但是如何定义最佳的权衡呢…