我的 JavaScript,比你的 Rust 更快

Josh Urbane 是一位从业多年的软件架构师,很喜欢在社交媒体分享技术观点。近日,他写了一篇文章,记录了自己凭借经验赢了与新人开发者打赌的故事,而“我的 JavaScript 比你的 Rust 更快”的结论也是来自这个打赌。他的故事或许可以说明运行策略在研发实践中的重要性。

对我来说,软件架构师这活儿最让人开心的一点就是能指导开发者理解最新的概念、影响他们的技术判断。有些开发者不是很嚣张吗,那就用理论加现实啪啪打他们的脸;架构师还得负责营造出寓教于乐的学习氛围,帮助年轻气盛的开发者逐渐长大成熟。

最会让我在心里暗爽的事儿就是一个愣头青开发者突然跳出来、想要挑战我的技术建议(从开发者的视角看,架构师就是一帮总在提「错误」建议的傻瓜),而且赌上全部身家坚持认为自己的办法更好。

问题是,我已经干这行很久了,不用验证我就知道问题的正确答案是什么。所以那就来呗,咱们手底下见真章,我把这段故事记录了下来、在几年后整理成了今天的这篇文章。

老实讲,下面要讲的这个事已经过去好几年了,所以很多细节我已经记不清楚。大体情况就是结合当时团队的知识储备、可用工具库和原有技术债务,我给出的建议是让大家使用 Node.js。

一个新任初级开发者对自己刚拿到的计算机科学学士证书很有信心,想要用“炫技”的方式挫挫我的锐气。他们听说我是辅修的计算机科学,所以觉得我压根不了解计算机底层原理。其实刚毕业那会我也认为自己很懂,但这行干久了,我越来越觉得计算机系统像是魔法……

他的信心并非毫无来由,这个结论如同“C++比 JavaScript 速度快”,基本属于业界共识。但作为典型的架构师,我仍然坚持认为“要视情况而定”。

更具体地讲,“经过充分优化的 C++,确实比具有同等优化水平的 JavaScript 跑得更快”,毕竟 JavaScript 有着无法避免的执行开销(即便如此,我们也可以把代码编译成静态程序来获得高度接近 C++的性能)。反正话已至此,那就梭了呗。

意外的是,JavaScript 代码确实要比 C++版本更快一点,而且从架构设计的角度来看,JS 版本可以由当前团队一力维护、不需要借助其他部门的技术能力。

还好还好,其实我也不敢百分之百确定自己是对的,但考虑到这个用例中的内存对象大小可能是动态的、再加上那位年轻开发者确实经验不足,所以我愿意赌上一把。

我猜大多数开发者都理解不了这样的结果。这明显跟“编译”语言快于“解释”语言、“静态”程序快于“VM”程序的基本原则背道而驰啊。但请注意,这些只是经验、而非真理。

我之前也提到,“优化”才是决定速度的关键。毕竟即使 C++语言自身的性能优势再强,糟糕的编写质量也会让程序身陷泥潭。另一方面,Node.js(使用基于 C++/C 的 V8 与 libuv 库)则更具优化空间,所以实际运行速度并不差。甚至可以说,质量同样差劲的 JS 和 C++程序,JS 的性能可能还更好一点。但这只是宏观论述,下面咱们来看点细节。

大多数开发者应该很熟悉栈和堆的概念,但这种理解基本只停留在了表面——例如只知道栈是线性的,而堆就是带有指针的“坨”(并非严格术语,大家能理解就行)。

更重要的是,栈和堆的概念对应着多种实现和方法。底层硬件并不知道“堆”是个什么东西,因为内存的管理方式是由软件来定义的,而内存管理方面的选择必然会对程序的最终性能产生巨大影响。

大家也可以就这个问题深挖下去,很有意义也很有价值。现代硬件和内核都相当复杂,其中往往包含大量具有特殊用途的优化机制,例如更高效地利用高级内存布局。这意味着软件可以(或者必须)借用由硬件提供的内存管理功能。此外还有虚拟化的影响……这里就不多做展开了。

没错,Node.js 解决方案的启动时间肯定更长,因为它需要通过 JIT 编译器来实现脚本的加载和运行。不过一旦加载完成,Node.js 代码其实反而拥有一项神秘的优势——垃圾回收机制。

而在 C++程序中,应用程序往往会在堆中创建动态大小的对象,之后再将其删除。这意味着程序的分配器必须一遍又一遍地在堆中分配和释放内存。这项操作本身速度较慢,而且实际性能基本由分配器中的算法决定。在多数情况下,dealloc 的速度会特别慢,即使是精简后的 alloc 也没好太多。

对于 Node.js 程序,这项绝技就是程序只运行一次就会退出。Node.js 同样运行脚本并分配必要的内存,但后面的删除操作会由垃圾回收器挑选空闲时间再推迟执行。

诚然,垃圾回收机制在本质上并不比其他内存管理策略更好或者更差(一切都是权衡),但在我们打赌的这个特定程序中,垃圾回收确实能显著提升性能,因为这个程序压根就没真正运行过。我们只是把一大堆对象塞进内存,再在退出时一次性丢弃。

垃圾回收肯定是有代价的,Node.js 进程占用的内存容量明显大于 C++程序。这就是“省 cpu=费内存”和“省内存=费 cpu”的经典难题,但我的目标就是打那小子的脸,所以费点内存也无所谓。

而我之所以能赢,是因为对方选择了一个幼稚的策略。其实他要想赢,最好的办法就是添加内存泄漏,故意把所有分配都保留在内存当中。这样 C++程序的内存占用量还是更小,但速度却比原先快得多。或者,他也可以用给栈分配缓冲区之类的设计来进一步提高性能,这种办法在实际生产中其实经常用到。

另外还有如何选择性能基准的问题。一般来说,大家比较的就是每秒操作数量。这里的 JS 对 C++就是个很好的例子,证明了“先理解总体性能成本,再做选择”往往更加靠谱。在软件架构中,我们必须得时刻关注资源层面的“总体拥有成本”。

Rust 是我目前最喜欢的语言之一。它提供了很多现代特性、速度很快,而且具备良好的内存模型,生成的代码也相当安全。

Rust 当然不是完美的,它的编译时间比较长、涉及不少奇奇怪怪的语义,但总体来说还是值得推荐。大家可以对 Rust 中的内存管理方式进行灵活控制,但其“栈”内存始终遵循所有者模型(ownership model),这也是其实现引以为傲的高安全性能的基础。

我目前参与的一个项目就是用 Rust 编写的 FaaS(函数即服务)主机,负责执行 WASM(WebAssembly)函数。它能快速安全地执行各项隔离函数,最大限度降低 FaaS 的运行开销。它的速度也很快,每核心每秒能够处理 90000 个简单请求。更重要的是,它的总内存占用量只有 20 MB 上下,可以说相当夸张了。

但这跟 Node.js 与 C++的赌局有什么关系?

简单来说,我是把 Node.js 视为“合理”的性能基准(Go 属于「梦幻」级基准,它的性能绝对不是那些专为 Web 服务设计的语言能比肩的,这里就别降维打击了),毕竟我们那款程序的早期 C++版本性能实在不咋的,唯一的好处就是内存占用量只有 Node.js 版本的不到十分之一。

虽然先让代码跑起来、再对代码做优化确实没啥毛病,但在 C++这种“快”语言上输给了 JavaScript 肯定让人非常沮丧。而我之所以敢当场,靠的就是对明显瓶颈的基本判断。这个瓶颈就是内存管理。

每个 guest 函数都被分配到一个内存数组,但在函数之内分配内存,以及在函数内存与主机内存间复制数据肯定会带来大量性能开销。由于动态数据被四处乱扔,分配器相当于是饱受四面八方的重拳打击。至于解决办法嘛,作弊喽!

从本质上讲,堆代表的是分配器用来管理映射的一部分内存。程序会请求 N 个内存单元,分配器在可用的内存池里搜寻这些单元(或者向主机请求更多内存)及存储哪些单元已被占用,之后再返回该内存的位置指针。当程序用尽内存时,就会告知分配器,再由分配器更新映射以明确现在哪些单元已经再次可用。挺简单的,对吧?

但如果我们需要分配一大堆生命周期有别、大小各异的内存单元时,麻烦就来了。这一定会产生大量碎片,进而放大了新内存的分配成本。于是性能损失开始产生,毕竟分配器的功能太过简单,只是在寻找可用的存储位置。

这个问题显然没有太好的解决方案,虽然目前可选的分配算法很多,但它们还是各有权衡、要求我们结合用例特点选择最适方法(也可以像大多数开发者一样,直接用默认选项)。

再来说作弊。作弊的办法可不只一种:对于 FaaS,我们可以释放每次运行的 dealloc,并在每次运行完成后清除整个堆;我们也可以在函数生命周期的不同阶段使用不同的分配器,例如明确区分初始化阶段和运行阶段。这样无论是干净的函数(每次运行,都会被重置为相同的初始内存状态)还是有状态函数(在每次运行之间保留状态),都能获得与之对应且经过优化的内存策略。

在我们的 FaaS 项目里,大家最终构建了一个动态分配器,它会根据使用情况选择分配算法、且实际选择会在每次运行之间持续留存。

对于“使用率较低”的函数(也就是大多数函数),只使用简单的栈分配器用指针指向下一个空闲槽即可。当调用 dealloc 时,如果该单元为栈上的最后一个单元,则回滚指针;如果不是最后一个单元,则无操作。当函数完成时,指针将被设置为 0(相当于 Node.js 在垃圾回收前退出)。如果函数的 dealloc 失败数和用量达到一定阈值,则在其余调用中使用其他分配算法。结果就是,这套方案在大多数情况下都能显著加快内存分配。

运行时中还用到了另一个“堆”——主机(或者说是函数共享内存)。它使用同样的动态分配策略,并允许绕过早期 C++版本中的复制步骤、直接写入函数内存。如此一来,I/O 就能直接从内核中复制 guest 函数,并绕过主机运行时,从而显著提高吞吐量。

经过优化,Rust FaaS 运行时最终比我们的 Node.js 参考实现快了 70%以上,而内存占用量更是不到后者的十分之一。

但这里的关键在于“经过优化”,它的初始实现其实速度反而更慢。我们的优化还要求对 WASM 函数做出一些限制,具体限制在编译过程中完全公开透明,而且极少出现不兼容的情况。

Rust 版本的最大优势就是内存占用小,省下来的 RAM 可以用作缓存或者分布式内存存储等其他用途。这意味着 I/O 开销进一步降低,生产运行的效率更高,其效果甚至比拉高 CPU 配置还更明显些。

后续我们还有更多优化计划,但主要是为了解决主机层中一些具有重大安全影响的问题。虽然跟内存管理或者性能没啥关系,但毕竟也算支持了 “Rust 比 Node 更快”党们的观点。

其实全文写下来,我也得不出特别明确的结论。下面只给出几个粗浅的观点:

  • 内存管理很有趣,每种方法都是在做取舍。只要策略运用得当,任何一种语言都能获得巨大的性能提升。
  • 我仍然推荐大家根据实际目标灵活使用 Node.js 和 Rust,所以这里不做优劣判断。JavaScript 的可移植性确实更好,而且特别适合云原生开发场景;但如果大家特别看重性能,那 Rust 可能是个更好的选择。
  • 从头到尾我都在说 JavaScript,但这里实际指的是 TypeScript。

归根结底,大家得根据实际情况选择最适合的技术方案。我们越是了解不同栈的不同特征,在选择的时候就越是从容有数。

原文链接:

https://medium.com/@jbyj/my-javascript-is-faster-than-your-rust-5f98fe5db1bf

了解更多软件开发与相关领域知识,点击访问 InfoQ 官网:https://www.infoq.cn/,获取更多精彩内容!

一篇文章带你快速入门JavaScript(实操代码)

JavaScript是一种动态的计算机编程语言。它是轻量级的,最常用作网页的一部分,其实现允许客户端脚本与用户交互并创建动态页面。它是一种具有面向对象功能的解释型编程语言。

Javascript和Java没有任何关系,它们是不同的两种语言(java是一种程序设计语言,javascript 是客户端的脚本语言),只是名字上都有一个Java而已。

对了,在这里说一下,我目前是在职web前端开发,如果你现在正在学习前端,了解前端,渴望成为一名合格的web前端开发工程师,在入门学习前端的过程当中有遇见任何关于学习方法,学习路线,学习效率等方面的问题,都可以随时关注并私信我:前端,我都会根据大家的问题给出针对性的建议,缺乏基础入门的视频教程也可以直接来找我,我这边有最新的web前端基础精讲视频教程, 还有我做web前端技术这段时间整理的一些学习手册,面试题,开发工具,PDF文档书籍教程,都可以直接分享给大家。

这三个要素共同构成了Web开发的基础。

HTML:页面的结构-标题,正文,要包含的任何图像CSS:控制该页面的外观(这将用于自定义字体,背景颜色等)JavaScript:不可思议的第三个元素。创建结构(HTML)和美学氛围(CSS)后,JavaScript使您的网站或项目充满活力。

  1. 表单数据验证:表单数据验证是JavaScript最基本也是最能体现效率的功能。
  2. 动态HTML(即DHTML):动态HTML指不需要服务器介入而动态变化的网页效果,包括动态内容、动态样式、动态布局等。 比如改变盒子的尺寸,背景颜色,图片等。
  3. 用户交互:用户交互指根据用户的不同操作进行的响应处理。例如:联动菜单等。
  4. 数据绑定:HTML中表单和表格能够以.txt文件定义的数据源,通过对位于服务器端的数据源文件的访问,便可以将数据源中的数据传送到客户端,并将这些数据保存在客户端。
  5. 少量数据查找:能够实现在当前网页中进行字符串的查找和替换。
  6. AJAX核心技术:AJAX即异步JavaScript+XML。该对象提供一种支持异步请求的技术,使客户端可以使用JavaScript向服务器提出请求并处理响应,但并不影响用户在客户端的浏览。
  7. Nodejs就是使用的javascript做后端,是目前为止唯一的一个既能做前端、又能做后端的语言。

(上面这个作用是直接用的我的老师的课件,我可没这么6懂这么多。他一个10多年开发经验的资深程序员哈哈哈哈哈哈,有点想帮忙宣传一下他的网课,但想想还是算了吧,感觉打广告有点不好)

*********************************************一条华丽的分割线***************************************************

1. js程序必须写在script标签中。2. script:可以写在网页中的任何位置。3. type=“text/javascript”:表示当前的语言是javascript语言。这个属性是可以省略的

举例:上代码

拿代码去运行一下就知道了

创建一个js文件,在js文件中编写js代码。(外部文件中编写js代码就直接写代码就可以了,不用再添加script标签)

比如说在js目录下面创建一个 test.js文件 里面的代码为alert(“出错啦!”)

举例上代码

a.html

拿代码去运行一下就知道了

举例上代码:

运行效果拿去试试就知道了,点一下第一个小盒子

  1. 在js中,使用关键字function可以定义一个函数,函数里面的代码不会自动执行,只有函数被调用后,函数里面的代码才会执行。
  2. 可以给网页中的任何html容器标签绑定点击事件。οnclick=“add();” onclick表示点击的时候执行。
  3. js中有两个函数parseInt 将字符串转为数字。 parseFloat():将字符串专为浮点类型。

举例上代码

拿去运行一个就知道了哈哈哈哈,这个学会了,下面那个就容易多啦!*********************************************一条华丽的哈哈哈哈哈哈哈哈***************************************************

实现效果:点击什么颜色代表的小框框,就会弹出穿啥衣服的 fairy

(哈哈哈哈 本人敲爱看这些美丽的事物哈哈哈哈)

自己可以下载一些图片或者颜色渐变图片用来做背景,放在img里面,可自己命名。基本格式如下图:

上代码:

网页换肤.html

css1.css

css2.css

css3.css

一些很基础的东西,要是写起来那就太多了,很多不常用的,到了我们需要它的时候谷歌和百度就行了。由于时间关系,暂时更到这里。

原文链接:https://link.zhihu.com/?target=https%3A//blog.csdn.net/hanhanwanghaha/article/details/109188646

作者:我一个超级无敌可爱的人鸭

出处:CSDN

初学JS必会的10种JavaScript代码优雅写法

当我们刚开始学习JS代码时,我们只需要掌握JS中对应知识点就好,随着对JS代码的熟悉程度,我们就要思考如何写出更优雅,更简洁的代码。

接下来我分享10种常用JS代码功能,通过常规写法和优雅写法的对比,来体现其优雅和简洁性。代码中用了ES6新特性,如果你对ES6不了解,可以先收藏好。在后期的VUE中,基本都在和ES6打交道。

常规写法

利用concat方法来合并数组

优雅写法

利用ES6中的…扩展运算符来合并数组

常规写法

利用数组下标一个一个从数组中取数据

优雅写法

利用ES6的解构赋值来取值

常规写法

对象.属性名 的方式获取属性值

优雅写法

利用ES6的解构赋值来实现

常规写法

利用for循环来遍历数组,从而取值

优雅写法

利用ES6的for … of来遍历数组取值

常规写法

forEach中回调函数为普通函数

优雅写法

forEach中回调函数为箭头函数,如果箭头函数中只有一句代码,则可以省略{ }

常规写法

数组中保存着每一条水果的信息,我们通过输入水果名,到数组中查找到对应的信息。

利用常规的for循环遍历来查找。

优雅写法

利用数组的find方法来实现搜索

常规写法

利用parseInt来实现

优雅写法

利用+ 号来实现,不过只针对纯数字的字符串有效

常规写法

通过if判断,如果为null,则初始化值为“普通用户”

优雅写法

通过 || 或短路运算符来实现

常规写法

优雅写法

常规写法

利用for循环来遍历

优雅写法

利用es6的扩展运算符和解构赋值来实现

最后我想告诉大家一个好消息,为了帮助关注我的同学,我们创建了《30天挑战学习计划》,全程免费,不涉及任何费用和利益,具体内容为以下4部分

1、HTML5+CSS3核心知识

2、30个HTML5+CSS3案例

3、2个PC+移动+响应式综合项目实战

4、网站全面上云部署与发布

接下来我来详细介绍下这个课程体系!

为帮助到一部分同学不走弯路,真正达到一线互联网大厂前端项目研发要求,首次实力宠粉,打造了《30天挑战学习计划》,内容如下:

HTML/HTML5,CSS/CSS3,JavaScript,真实企业项目开发,云服务器部署上线,从入门到精通

  • PC端项目开发(1个)
  • 移动WebApp开发(2个)
  • 多端响应式开发(1个)

共4大完整的项目开发 !一行一行代码带领实践开发,实际企业开发怎么做我们就是怎么做。从学习一开始就进入工作状态,省得浪费时间。

从学习一开始就同步使用 Git 进行项目代码的版本的管理,Markdown 记录学习笔记,包括真实大厂项目的开发标准和设计规范,命名规范,项目代码规范,SEO优化规范

从蓝湖UI设计稿 到 PC端,移动端,多端响应式开发项目开发

  • 真机调试,云服务部署上线;
  • Linux环境下 的 Nginx 部署,Nginx 性能优化;
  • Gzip 压缩,HTTPS 加密协议,域名服务器备案,解析;
  • 企业项目域名跳转的终极解决方案,多网站、多系统部署;
  • 使用 使用 Git 在线项目部署;

这些内容在《30天挑战学习计划》中每一个细节都有讲到,包含视频+图文教程+项目资料素材等。只为实力宠粉,真正一次掌握企业项目开发必备技能,不走弯路 !

过程中【不涉及】任何费用和利益,非诚勿扰 。

如果你没有添加助理老师微信,可以添加下方微信,说明要参加30天挑战学习计划,来自头条号!老师会邀请你进入学习,并给你发放相关资料

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。