整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

JavaScript、Python 比 C++ 慢

JavaScript、Python 比 C++ 慢 8 倍、29 倍?
整理 | 苏宓
出品 | CSDN(ID:CSDNnews)

近日,来自多伦多大学和 YScope 公司(为软件系统提供创新的日志管理和故障排除工具。由一群计算机工程教授和博士创立)的 David Lion、多伦多大学 Adrian Chiu 和 Michael Stumm、多伦多大学和 YScope 公司 Ding Yuan 共同发布了一份《调查托管语言的运行时性能:为什么 JavaScript 和 Python 比 C++ 慢了 8 倍和 29 倍,而 Java 和 Go 却能更快》(https://www.usenix.org/system/files/atc22-lion.pdf)的论文分析报告,深度剖析了不同编程语言运行时在代码开发中真实的性能情况,由此方便开发者可以精确地测量执行任何字节码指令所花费的时间等。


性能是系统软件不得不面对的挑战


在报告中,研究人员指出,自 2015 年以来,具有集成运行时环境的编程语言越来越受欢迎,其中,全球知名的代码托管平台 GitHub 上最受欢迎的三种语言分别是 JavaScript、Java 和 Python。

作为开发利器,编程语言帮助开发者快速构建各种应用程序和服务,也极大地提高了生产力。同时,这些语言自身也提供了各种功能,如动态类型检查、带有垃圾收集的内存管理,以及动态内存安全检查等等。为此,研究人员用「托管语言」(managed languages)专业术语来指代这些类型的编程语言。

现实来看,托管语言越来越多地被用于实现性能至关重要的系统软件上,如Hadoop 和 Spark 都在 Java 虚拟机(JVM)上运行,因为它们分别用 Java 和 Scala 实现;Kubernetes、etcd(分布式键值存储)和 M3(由 Uber 建立的分布式时间序列数据库和查询引擎)都是用 Go 实现的。

当前,甚至连操作系统(OS)的内核 Biscuit 也是用 Go 实现的 。Openstack、Paypal、Instagram 和 Dropbox 都大量使用 Python,其中,Python 是 Dropbox "在后台服务和桌面客户端应用中使用最广泛的语言",在一个存储库中就有近 400 万行 Python 代码;JavaScript 也被用于 Facebook 的 Bladerunner pub/sub 系统的性能关键路径中。

在开发过程中编程的性能在一开始很少会被考虑到项目中部分原因是不少开发者认为性能问题可以在以后慢慢去解决,也许可以通过简单地增加硬件来进行横向扩展

不过,随着代码品或服务使用规模的扩大,服务变得越来越慢或者硬件成本变高,性成为一个不容忽视的问题这也是为什么 Stream 要放弃了 Python 而改用 Go、 Discord 从 Go 切换到 Rust、Twitter 从 Ruby on Rails 切换到 Scala 和 Java 的主要原因。

不少开发者往往为了提升性能,想破脑袋,但现实只有两条路,一条是从现有的代码中想尽办法尽可能地做优化,另一条是思考使用的编程语言是否已经达到了性能极限,看看有没有必要将旧的代码移植到一个新的性能更高的语言上。

为了彻底解开系统软件中不同编程语言导致的性能问题,研究人员决定以 C++ 为极限,对 Java、Go、JavaScript Python 四种编程,还有应用最广泛的运行时系CPython、OpenJDK。Node.js 与 JavaScript 的 V8 引擎行深入的定量性能分析。

同时,研究人员还从头开始建立了 6 个应用程序,并创建了一个名为

洞最有可能影响使用WebKit呈现引擎显示网页的任何IOS和macOS应用程序。到目前为止Apple仍然在调查。安全研究人员发现Safari使用的WebKit渲染引擎中存在一个漏洞,该漏洞会令使用IOS操作系统的iPhone和iPad系统崩溃并重新启动。

可以通过加载使用特制CSS代码的HTML页面来利用此漏洞。CSS代码不是很复杂,并尝试将一种称为backdrop-filter的CSS效果应用于一系列嵌套页面段(DIV)。

背景过滤器是一种相对较新的CSS属性,通过模糊或颜色移动到元素后面的区域来工作。这是一项繁重的处理任务,一些软件工程师和Web开发人员推测,这种效果的渲染会对IOS的图形处理库造成影响,最终导致移动操作系统崩溃。

Sabri Haddouche是加密即时消息应用程序Wire的软件工程师和安全研究员,也是他发现了这个漏洞,并在近日早些时候在Twitter上发布了概念验证代码。

此链接将使您的iOS设备崩溃,而此链接将显示此漏洞背后的源代码。Haddouche还在推特上发布了一个漏洞导致手机崩溃的视频短片。Haddouche 在采访中称“攻击使用webkit-backdrop-filter CSS属性中的弱点,该属性使用3D加速来处理它们背后的元素”。“通过使用具有该属性的嵌套div,我们可以快速消耗所有图形资源并冻结或内核恐慌OS。”Haddouche还表示该漏洞也会影响macOS系统,而不仅仅是iOS。

研究人员告诉媒体说:“利用当前的攻击(仅限CSS / HTML),它只会冻结Safari一分钟然后放慢速度。” “之后你就可以关闭标签了。”“为了使它适用于macOS,它需要一个包含Javascript的修改版本,” “我没有发布它的原因是,似乎Safari在强制重启后仍然存在并且浏览器再次启动,因此在恶意页面再次执行时会破坏用户的会话。”

研究人员表示,在Twitter上发布代码之前,他已经通知了Apple这个问题。“我使用他们的安全产品电子邮件联系了他们,”Haddouche告诉媒体“他们证实他们收到了这个问题并正在调查它。”

Haddouche告诉媒体称他在研究多个浏览器上可靠的拒绝服务(DoS)错误时发现了这个漏洞。在本月初,Haddouche还发布了另一个利用一行JavaScript破坏Chrome和Chrome OS的漏洞。

另一方面,正如一位iOS开发人员告诉媒体的那样,这个漏洞可能比以前想象的存在更广泛。这是因为Apple强制App Store上列出的所有浏览器和支持HTML的应用程序都使用其WebKit渲染引擎,这意味着该问题很可能会导致任何能够加载网页的应用程序崩溃。

目前苹果公司还未对这一事件做出正面回应。

里有一些 Javascript初学者应该知道的技巧和陷阱。如果你已经是专家了,顺便温习一下。

Javascript也只不过是一种编程语言。怎么可能出错嘛?

1. 你有没有尝试给一组数字排序?

Javascript 的sort()函数在默认情况下使用字母数字(字符串Unicode码点)排序。

所以[1,2,5,10].sort() 会输出 [1, 10, 2, 5].

要正确的排序一个数组, 你可以用 [1,2,5,10].sort((a, b)=> a?—?b)

很简单的解决方案, 前提是你得知道有这么个坑

2. new Date() 很棒

new Date() 可以接受:

  • 没有参数: 返回当前时间

  • 一个参数 x: 返回1970年1月1日 + x 毫秒。 了解 Unix 的人知道为什么。

  • new Date(1, 1, 1) 返回 1901, 二月 , 1号/。因为,第一个参数表示1900年加1年,第二个参数表示这一年的第二个月(因此是二月)?—?脑回路正常的人会从1开始索引?—?,第三个参数很明显是这个月的第一天,所以1?—?有时候索引确实从1开始?—?。

  • new Date(2016, 1, 1) 不会给1900年加上2016。它仅代表2016年。

3. Replace 并不“替代”

let s="bob"
const replaced=s.replace('b', 'l')
replaced==="lob"
s==="bob"

我觉得这是一件好事,因为我不喜欢函数改变它们的输入。 你还应该知道 replace 只会替换第一个匹配的字符串:

如果你想替换所有匹配的字符串,你可以使用带/g标志的正则表达式 :

"bob".replace(/b/g, 'l')==='lol' // 替换所有匹配的字符串

4. 比较的时候要注意

// These are ok
'abc'==='abc' // true
1===1 // true
// These are not
[1,2,3]===[1,2,3] // false
{a: 1}==={a: 1} // false
{}==={} // false

原因:[1,2,3]和[1,2,3]是两个独立的数组。它们只是恰好包含相同的值。它们具有不同的引用,无法用===相比较。

5. 数组不是原始数据类型

typeof {}==='object' // true
typeof 'a'==='string' // true
typeof 1===number // true
// But....
typeof []==='object' // true

如果你想知道你的变量是不是数组,你仍然可以用Array.isArray(myVar)

6. 闭包

这是一个很有名的面试题:

const Greeters=[]
for (var i=0 ; i < 10 ; i++) {
Greeters.push(function () { return console.log(i) })
}
Greeters[0]() // 10
Greeters[1]() // 10
Greeters[2]() // 10

你是不是认为它会输出 0, 1, 2… ? 你知道它为什么不是这样输出的吗? 你会怎样修改让它输出 0, 1, 2… ?

这里有两种可能的解决方法:

用 let 替代 var. Boom. 解决了.

let和var的不同在于作用域。var的作用域是最近的函数块,let的作用域是最近的封闭块,封闭块可以小于函数块(如果不在任何块中,则let和var都是全局的)。(来源)

替代方法: 用 bind:

Greeters.push(console.log.bind(null, i))

还有很多其他方法。这只是我的两个首选

7. 谈到 bind

你认为这个会输出什么?

class Foo {
constructor (name) {
this.name=name
}
greet () {
console.log('hello, this is ', this.name)
}
someThingAsync () {
return Promise.resolve()
}
asyncGreet () {
this.someThingAsync()
.then(this.greet)
}
}
new Foo('dog').asyncGreet()

如果你认为这个程序会崩溃提示 Cannot read property 'name' of undefined,给你一分。

原因: greet 没有在正确的上下文中运行。同样,这个问题依然有很多解决方案。

我个人喜欢

asyncGreet () {
this.someThingAsync()
.then(this.greet.bind(this))
}

这样可以确保类的实例作为上下文调用greet。

如果你认为greet 不应该在实例上下文之外运行, 你可以在类的constructor中绑定它:

class Foo {
constructor (name) {
this.name=name
this.greet=this.greet.bind(this)
}
}

你还应该知道箭头函数(=> )可以用来保留上下文。这个方法也可以:

asyncGreet () {
this.someThingAsync()
.then(()=> {
this.greet()
})
}

尽管我认为最后一种方法并不优雅。

我很高兴我们解决了这个问题。

总结

祝贺你,你现在可以放心地把你的程序放在互联网上了。甚至运行起来可能都不会出岔子(但是通常会)Cheers \o/