百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

postMessage 性能飙升500倍,内存占用锐减95%?

myzbx 2025-10-19 10:03 5 浏览

家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。

postMessage 是 JavaScript 在多个工作线程之间发送数据的最常用方法,在 Bun v1.2.21 中,postMessage(string) 的性能做到了几乎与字符串大小无关,这对于多线程 JavaScript 服务器和命令行界面 (CLI) 来说是一项重大的性能改进。

 // 主页面发送消息和接收消息
function sendMessage() {
    document.getElementById('iframe').contentWindow.postMessage('你好,子页面!', '*');
}
window.addEventListener('message', function(event) {
    document.getElementById('messages').innerHTML += '<p>收到: ' + event.data + '</p>';
});
// 子页面发送消息和接收消息
function sendMessage() {
    window.parent.postMessage('你好,父页面!', '*');
}
window.addEventListener('message', function(event) {
    document.getElementById('messages').innerHTML += '<p>收到: ' + event.data + '</p>';
});

通过避免对可以安全跨线程共享的字符串进行序列化,postMessage 速度提高了 500 倍,并且在此基准测试中使用的峰值内存减少了约 22 倍。

当开发者在工作线程之间发送字符串时,优化会自动启动:

// 在worker之间发送数据
const response = await fetch("https://api.example.com/data");
const json = await response.text();
postMessage(json); 
// 大型字符串带来500x的性能提升

这对于在工作线程之间传递大型 JSON 的应用程序特别有用,例如: API 服务器、数据处理管道和实时应用程序。

如何在 JavaScriptCore 中实现这一点

postMessage 通常使用结构化克隆算法对数据进行序列化,然后再将其发送到另一个线程。这意味着需要将字符串的每个字节复制到一个新的缓冲区,然后在另一端对其进行反序列化。

// 原始对象
const original = {
    name: "张三",
    age: 25,
    hobbies: ["读书", "游泳"],
    details: {
        city: "北京"
    }
};
// 使用 structuredClone 深拷贝
const cloned = structuredClone(original);
// 修改克隆对象,不会影响原始对象
cloned.name = "李四";
cloned.hobbies.push("编程");
cloned.details.city = "上海";
console.log("原始对象:", original);
// 输出: { name: "张三", age: 25, hobbies: ["读书", "游泳"], details: { city: "北京" } }
console.log("克隆对象:", cloned);
// 输出: { name: "李四", age: 25, hobbies: ["读书", "游泳", "编程"], details: { city: "上海" } }

但问题是,在 JavaScriptCore(Bun 使用的引擎)中,字符串已经是线程安全的引用计数对象。字符串数据本身在创建后是不可变的,并且引用计数使用 std::atomic:

class StringImplShape {
    std::atomic<unsigned> m_refCount;  // 线程安全
    unsigned m_length;                  // 不可变
    union {
        const LChar* m_data8;           // 不可变
        const char16_t* m_data16;       // 不可变
    };
    mutable unsigned m_hashAndFlags;    // 唯一可变的部分
};

因此,如果字符串已经是线程安全的,那么在同一进程的线程之间发送时可以有效避免序列化

寻找快速路径

并非所有字符串都能安全地共享,主要包括三种需要序列化的类型:

  • 原子字符串 :线程本地属性名称和Symbol
  • 子字符串 :指向具有复杂生命周期的其他字符串
  • Rope 字符串 : 由“foo”+“bar”或 .slice() 等操作创建的字符串

对于其他字符串可以完全跳过序列化,只需确保在共享之前惰性计算哈希值,因为这是唯一可变的部分,例如:

WTF::String toCrossThreadShareable(WTF::String& string)
{
    auto* impl = string.impl();
    // 不能共享原子字符串,symbol和substring字符串
    if (impl->isAtom() || impl->isSymbol() ||
        impl->bufferOwnership() == StringImpl::BufferSubstring)
        return string.isolatedCopy();
    // 在共享之前计算hash
    impl->hash();
    // Prevent this thread from atomizing.
    impl->setNeverAtomicize();
    return string;  
    // 直接共享指针
}

快速路径条件

优化适用于以下情况:

  • 使用 postMessage 或 StructuredClone
  • 仅发送字符串而非混合数据
  • 非子字符串、Row、Atom 或 Symbol
  • 向同一进程中的另一个线程发送数据
  • string.length >= 256 个字符

以上场景涵盖了在 Worker 之间发送字符串的极其常见的模式,这也是性能提升如此显著的原因。

参考资料

https://bun.com/blog/how-we-made-postMessage-string-500x-faster

https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/structuredClone

相关推荐

别再问Cookie了,再问就崩溃了!_别问 再问

作者:懿来自:Java极客技术说实话,之前面试都是直接去背诵的面试题,关于Cookie的一些内容,比如说,记录浏览器端的数据信息啦,Cookie的生命周期啦,这些内容,也从来没有研究过C...

5分钟学会物流轨迹地图API嵌入到页面中,实现物流轨迹可视化

前言在电子商务和在线购物日益普及的今天,为用户提供实时的物流信息已成为提升客户满意度的关键。本文将指导您如何在网页中嵌入物流轨迹地图API,以便用户能够直观地跟踪他们的包裹。1.申请接口、获取API密...

Springboot项目中几种跨域的解决方法

环境:springboot2.3.9.RELEASE什么是跨源资源共享跨源资源共享(CORS)(或通俗地译为跨域资源共享)是一种基于HTTP头的机制,该机制通过允许服务器标示除了它自己以外的其它...

基于Java实现,支持在线发布API接口读取数据库,有哪些工具?

基于java实现,不需要编辑就能发布api接口的,有哪些工具、平台?还能一键发布、快速授权和开放提供给第三方请求调用接口的解决方案。架构方案设计:以下是一些基于Java实现的无需编辑或只需少量编辑...

Axios VS Fetch, 用哪个更好?详细对比附案例

在JavaScript中进行HTTP请求时,最常用的两个工具是:原生fetchAPI流行的第三方库Axios我都在生产环境中使用过这两个工具。虽然两者都表现良好,但有时我会后悔选择了其中一个而非另一...

Ollama:Web搜索API和MCP_oalib search

如果您曾经尝试过LLM,您就会明白其中的痛点:模型在模式匹配方面非常出色,但往往会虚构一些东西。如果你问起上周发生的事情,突然间,您得到的只是来自2022年的鬼故事。这次更新改变了这一切。基本上...

基于浏览器扩展 API Mock 工具开发探索|得物技术

一、前言在日常开发过程中,偶尔会遇到后端接口未完成或者某个环境出现问题需要根据接口返回来复现等等场景。刚好最近在学习浏览器插件的相关知识,并在此背景下开发了一款基于浏览器插件的Mock工具。该工...

JavaScript动态注入的几种方法_js动态引入js

在现代的Web开发中,JavaScript动态注入是一个强大的技术,它允许开发者在网页运行时动态地修改网页内容和行为,方便进行调试和维护。动态注入通常涉及以下几个关键概念:DOM(文档对象模型)、和...

面试官:如何通过 MyBatis 查询千万数据并保证内存不溢出?

推荐学习真香警告!Alibaba珍藏版mybatis手写文档,刷起来牛掰!“基础-中级-高级”Java程序员面试集结,看完献出我的膝盖闭关28天,奉上[Java一线大厂高岗面试题解析合集],备战金九银...

nextjs教程三:获取数据_nextcloud数据迁移

数据的获取数据获取是任何应用程序中最重要的部分,本文将介绍,如何在react,nextjs中获取数据主要有种方法可以获取数据在服务端,用fetch获取数据在客户端,通过路由处理器获取数据下面分别...

Fetch API 教程_fetch_all

JavaScript初学者学完语法,进入实际的网页编程,一定有人告诉你,要掌握一个叫做XMLHttpRequest的东西。脚本都靠它发出HTTP请求,跟服务器通信。所谓的AJAX操作就是...

Mozilla火狐39.0正式版增加Emoji支持

2015-07-0310:41:43作者:李熙Mozilla旗下浏览器火狐(Firefox)39.0正式版在今日发布,新版在性能上改进不大,着重于浏览器的功能和细节改进:新版提升了Firefox...

如何设计前端监控sdk,实现前端项目全链路监控

一、埋点系统设计与实现(文章最后有如何回答)1.埋点分类1.1手动埋点(代码埋点)//业务代码中主动调用tracker.track('button_click',{&nbs...

如何快速实现一套流程编排系统,前端开发组件都有哪些,一篇搞懂

早上9点,AI产品经理紧急拉会:“我们的客户明天要看到AI审批流程原型,传统开发至少要一周,有什么办法今天就能上线?”这时,你打开流程编排画布,拖拽几个节点,连接大模型API和服务,1小时后客户竖起...

2023金九银十必看前端面试题!2w字精品!

导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。答案:CSS的盒模型是用于布局和定位元素的概念。它由内容区域...