為什么有些語言會比別的快?

摘要:為什么有些語言比別的快:JIT與字節碼的使用,帶來了Java和C#這樣介于前兩者之間的中間選擇;編譯型語言比解釋型快,引入的間接層越多,速度越慢;語言設施上的投入力度;語言本身的設計等等。


看看在科學計算方面,Fortran語言在科學和工程領域經久不衰,討論的最熱烈的一個主題就是性能。

Fortran語言至今依然非常重要的一個最主要原因是速度快。在Fortran中搗弄數字的方式比在其他語言中使用的別的方式要快。能在這個領域和Fortran競爭的——C和C++,被廣泛使用的原因也是因為他們性能的競爭力極強。

問題是,為什么?是什么讓C++和Fortran這么快?又是為什么,他們能勝過像Java或者Python這些其他語言?

解釋VS編譯

對不同編程語言分類的方法有很多,像是依據編程語言偏好的風格分類,或者它們所支持的特性分類等等。側重性能的話,可以分為編譯型語言和解釋型語言。

這兩種類型的區分并不困難;相反,就像是在一條線上的兩端。在一端,是傳統的編譯型語言,這一類包括Fortran,C/C++等。在這些語言中,有一個單獨的編譯平臺,專門負責將源代碼編譯成處理器可以執行的程序。

編譯的過程分為以下幾步。首先是分析和解析源代碼。基本的拼寫錯誤和筆下誤會在此時被檢測出來。檢查過的源代碼將用來產生存放在存儲器中的表達式,作用同樣是檢查錯誤——這一次檢查的是語法錯誤,例如調用了不存在的函數,或者在字符串或文本上進行了算數運算。

然后,存儲器中的表達式驅動代碼生成器,這一步會生成可執行的代碼。為了提高可執行代碼的性能,代碼優化會遵循一以下的過程來進行:在代碼的表達式上進行高水平的優化,在代碼生成器的輸出上進行低水平的優化。

事實上,隨后就可以執行生成的代碼。完整的編譯過程就是這樣。

在對面的一端,就是解釋型語言。解釋型語言也有類似于編譯器那樣的解析平臺,但是它的代碼直接用來驅動程序的運行。

最簡單的解釋器會將源代碼和各種各樣編程語言所支持的特性進行匹配——所以解釋型語言將會負責添加數字,添加到字符串等函數。隨著代碼被解析,解釋器會匹配到相應的函數并且執行它。程序中產生的變量將存儲在表中,通過名字和值的匹配來提供查詢。

解釋型語言的風格最極端的例子是批處理文件和shell script。在這些語言中,可執行的代碼通常不會構建到解釋器中,而是獨立的運行。

那么,到底是什么造成了這種性能的不同呢?通常來說,每一次底層間接的調用都會降低性能。例如,對兩個數字做加法最快的方式,是將其存放在處理器的寄存器中,然后使用處理器的加法指令。編譯器可以這樣做,可以將變量往寄存器里面存并利用處理器的指令。但是解釋器卻不行,相同的添加動作需要這樣來完成:聲明兩個變量的名稱與值,然后調用函數執行加法動作。相應的函數可能對處理器調用了一樣的指令做加法,所有這些在調用處理器指令之前的這些工作都讓性能變慢了。

模糊的界限

在這兩個極端之間還存在著別的選擇。例如,很多優秀的解釋器表現得像編譯器一樣:它們執行類似于編譯器的步驟,包括生成可以直接執行的代碼,但是它們緊接著就將代碼執行了(而不是存儲在硬盤上供以后執行)。在程序的運行期間,解釋器會保留這些可執行的代碼,這樣當需要特定代碼的時候就不用重新編譯了,但是程序執行完的時候,可執行代碼就被刪除了,如果你想再運行這個程序,就不得不重新生成可執行代碼。

這種在程序執行時候編譯帶啊的方法叫做即時編譯(Just-in-time,JIT),IE,火狐,Chrome瀏覽器的JavaScript引擎都使用了這項技術來提高它們的腳本性能。

即時編譯一般比傳統的解釋要高效。然而,卻比不上提前編譯(Ahead-of-time,AOT)。提前編譯可能會慢,因為編譯器要花大量的時間來,盡最大的努力來優化代碼。它們之所以能夠這樣做是因為人們大可不必在這段時間苦等著它們完成這項工作。然而,即時編譯卻是在運行時期,人們守在鍵盤前面等待程序運行。這就限制了優化代碼占用的時間。像是在后臺進程優化添加過程或者現在的多核處理器技術在這方面擁有廣泛前景。

原則上,即時編譯在改變編譯環境方面會表現出優勢。常規的編譯方式必須在某些方面非常保守。例如,微軟不能輕易地使用新一代英特爾和AMD上最新的AVX來編譯Windows,因為Windows必須要保證能夠在不支持AVX的處理器上運行。然而,一個即時編譯型的程序就可以,因為它能適應所處的硬件環境,并最大化的利用它。

歷史上,即時編譯并沒有很好地利用現代處理器提供的復雜指令集。的確,就算拋開時間的限制不說,用好像SSE或者AVX指令集對于提前編譯來說都是一個很大的挑戰。好在這種情況正在改善,比如Oracle的HotSpot Java虛擬機就有對這些指令集的早期支持。

另一種應用廣泛的技術是使用字節碼。基于字節碼的平臺有Java,.NET,他們也有傳統上的編譯,不過編譯器不生成可執行文件,而是生成字節碼,一種類型的字節碼并不是基于硬件環境而設計的,而是基于理想的虛擬機。程序運行的時候,字節碼可以被解釋或者即時編譯。

總體上說,這種基于字節碼系統的平臺介于編譯型和解釋型之間。字節碼即時編譯起來非常簡單并且易于優化,相比于解釋型語言是一個進步的地方,但優化代碼的效果仍比不上編譯型。

各種各種介于解釋型和編譯型中間的選擇,提供了廣泛的介于解釋型和編譯型兩種階段之間的選項。

技術上講,使用編譯器和解釋器并不是一個語言自身的特點。有一些項目,例如,有人為通常使用編譯器的C語言制作一個解釋器;JavaScript也正從簡單解釋器向復雜的即時編譯器過度以便強化其性能。

然而,主流的預編并不會在這兩種形式之間來回轉換。C++本質上說是提前編譯的,Fortran也是。C#和Java大多時候是編譯成字節碼,運行的時候再即使編譯。Python和Ruby通常是解釋型。這就產生了一個性能的分級:C++和Fortran比Java和C#快,Java和C#又比Python和Ruby快。

語言本身的特點

不同的語言種類之間依然存在著很大的性能差異,其中一個主要原因就是它們的受重視度。拿JavaScript和Python來說,例如:這兩個流行的腳本語言都是解釋型的。但實際上,JavaScript卻比Python要快的多,這并不是因為語言自身的特點——它們的表達式和能力方面不相上下——而是因為像微軟,谷歌,莫斯拉這些公司更加重視JavaScript。

幾種類似的語言,投資的不同(或者發展的優先級別不同)是一種語言性能的決定因素。寫一個優秀的編譯器或者解釋器需要花很多精力,而不是每一個語言都值得去這樣做。

然而,語言的不同也會對性能有所影響。Fortran語言的長壽就是一個很好的例子。很長一段時間,相同的兩個程序在Fortran和C(或者C++)中運行,Fortran會快一些,因為Fortran的優化做的更好。這是真的,就算C語言和Fortran的編譯器用了相同的代碼生成器也是一樣。這個不同不是因為Fortran的某種特性,事實上恰恰相反,是因為Fortran不具備的特性。

數據處理程序經常在很大的數量級上操作數據。內存中的數字會表示某個點在3D空間中或者其他的東西。運算通常會在這數據上對每一個元素迭代重復的操作。例如,有這樣一個函數:將兩個數組中的每一個元素添加對對方的數組中,需要三步:添加兩個數組,聲明第三個數組,然后進行運算獲得結果。

更好的添加數組可以用別的指令來完成,這允許這種各樣的優化。例如,可以使用SSE或者AVX這樣的指令集。不再是一個一個地添加元素,而是每一個添加四個,同時,使用SSE或者AVX指令集來做一個簡單的提升四倍性能的優化。這個函數可以應用到多線程:如果你有四核處理器,那么一個核心可以完成分四分之一的添加。這種基于數組的函數提供了一些強力的編譯的優化,使得代碼在運行很多次的時候,效率有了明顯的提升。

C語言其實并沒有讓數組作為函數的輸入(或者,在這種情況下,輸出)。而是使用了指針。指針或多或少地代表了地址,這是C語言的內嵌特點:讀出或者寫入儲存在內存中的地址值。在很多時候,C語言使用指針和數組可以互相轉換。對于數組,指針只是代表了元素的首地址。數組的其他元素就在下一個連續的內存中。C語言用內嵌的可以操作內存中的地址值的功能可以獲取數組。

指針非常復雜,C程序員使用它們來構建復雜的程序結構,以及緊湊有序的數組。但是這種復雜卻造成了編譯器優化的麻煩。依然以添加兩個數組的函數為例。在C中,可能不需要三個數組作為輸入,但是卻需要三個指針:兩個輸入,一個輸出。

問題就來了。這些指針可以代替任何內存地址。更重要的是,他們可以重疊。輸出數組的內存地址也可以同時是輸入數組的。甚至可以部分重疊,輸出數組可以覆蓋一個輸入數組的一半。

這對編譯器優化來說是個大問題,因為之前基于數組的優化不再適用。特別的,添加元素的順序也成問題,如果輸出重疊的數組,計算的結果會變得不確定,取決于輸出元素的動作是發生在元素被覆蓋之前還是之后。

這就意味著我們之前的理想的優化——使用多線程和迭代指令——行不通了。編譯器不知道這樣做是不是安全的,它處理器源碼的時候只好遵從原來的順序,沒有別的。編譯器不再有整理代碼使之更快的自由了。

這個問題叫做混淆現象,像傳統的Fortran語言就沒有這種問題。因為傳統Fortran沒有指針,它只有非重疊數組。很長時間以來,都允許Fortran的編譯器(或者程序員)對程序進行強有力的優化,不像在C。鞏固了Fortran在數據處理方面的霸主地位。

顯然對于這種功能的函數,這種指針的靈活性并不是很有用。如果數組重疊,就沒有合適的方式來處理數據,所以很不幸,優化在這里就毫無用武之地了。在1999年規定的C語言標準(C99),給出了這個問題的答案。在C99中,指針可以被指定為不重疊。這時編譯器就可以做所有的優化,C99的這個特性使得C語言(和C++,,因為大多數編譯器供應商給了一個類似的功能)成為了像Fortran那樣的可以優化的語言。

這個混淆現象表明語言的特點會跟優化相關,尤其是很大的、變革性的優化。例如,自動將單線程代碼轉換成多線程的代碼。然而,它也表明這種差異不是永久性的。開發人員希望能夠使用C和c++來處理數據,如果一些小的改變能夠使它像Fortran語言那樣快,開發人員就會做這樣一些改變。

原文鏈接: Why are some programming languages faster than others?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/447606.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/447606.shtml
英文地址,請注明出處:http://en.pswp.cn/news/447606.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Docker logs 查看實時日志(日志最后的N行、某刻后日志)

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 docker logs : docker logs -f -t --since"2017-05-31" --tail10 edu_web_1 edu_web_1 為容器名,也可…

研發團隊平穩度過“從小到大”并非易事

摘要:創業團隊在經歷初期的艱難發展階段后,團隊的擴大也帶來一系列管理上的混亂,CTO俱樂部會員、呱呱視頻社區研發總監王國良分享了自己關于創業團隊發展的思考,他認為研發團隊想要平穩度過“從大到小”并非易事。 創過業的人都知…

解決:idea - maven project 中 jar 報紅線

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 1. 如圖: 2. 找到報紅線的工程,執行 mvn clean后,再 mvn install 。這樣會從私服重新拉取 jar 。 …

云計算三大主流陣營的競爭與合作

摘要:目前,以AWS等為代表的互聯網陣營、以IBM、HP為代表的IT陣營以及以AT&T為代表的電信運營商陣營在云計算領域展開競爭與合作,但是未來三大陣營的競爭和合作將更加復雜,IaaS、PaaS和SaaS的界限也逐步消弭。 【編者按】云計…

springCloud - 第7篇 - 配置文件管理中心 ( SpringCloud Config )

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 一、簡介 分布式的系統往往有多個服務,會有不同的配置文件。 不同環境配置文件內容也各不相同: 開發環境、測試…

成功必讀本:培養人脈的106個技巧

成功的道路上,人脈比知識更重要。發展人際關系應當是你優先級最高的事。《不要一個人吃飯( Never Eat Alone)》一書介紹了21世紀的交際規則。書中包括了許多實用技巧,讓你通過人際關系而興旺發達。 保留精華,以下是從書中總結的106條技巧。在…

springCloud - 第8篇 - 配置文件管理中心 - 集群模式(負載勻衡)使用

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 當微服務系統中 應用服務有很多時(serviceA 、serviceB ...),會都從同一個配置中心讀取配置文件。此…

程序員的11個階段

程序員的職業生涯是一段充滿起伏的有趣經歷。考慮到其陡峭的學習曲線,完全可以預見你將經歷挫折、啟蒙、驕傲自大這幾個時期,以及穿插其間的各種心路歷程。在這篇文章中讓我們輕松一下,通過一系列搞笑 gif 圖來回顧其中的每一個階段 。 階段…

Docker 方式安裝 RabbitMQ (ribbitmq linux 部署)

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 1. 查找鏡像:(management 版本的鏡像才有管理界面) docker search rabbitmq:management2. 拉取鏡像…

中國股市6000點下跌創最大熊市 股民因貪欲被套牢

核心提示:人的欲望,他的那個欲望,他永遠在那個,你看漲了40我都不賣,還貪,還得到45,還貪。貪貪沒有完,跌到35了,35還覺得還能回去,跌30了。 鳳凰衛視5月14日《…

解決:c.r.c.impl.ForgivingExceptionHandler : An unexpected connection driver error occured

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 1. 我的情況: 在個人阿里云服務器上 docker 方式 安裝的 RabbitMQ,然后本地代碼連接一直報錯: 201…

中國股市低迷的根本原因

和訊網友“長江薦股”發文探討中國股市低迷的根本原因,我深表認同,特轉載于下: 1、當年股市為國企扭虧服務,給投資者埋下了后遺癥。1998年政府為了實現國企3年扭虧的目標,要求一家優質企業捆綁一家虧損企業一起上市&am…

解決:springcloud eureka 注冊的服務config-client 狀態Status:UNKNOWN (1)

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 1. eureka 注冊的服務config-client 的 Status 顯示為:UNKNOWN (1): 2.原因 : 配置信息 eureka.cl…

窮人靠力,富人借力!看完你將明白一切!

有個窮人,因為吃不飽穿不暖,而在佛祖面前痛哭流涕,訴說生活的艱苦,天天干活累的半死卻掙不來幾個錢。 哭了半晌他突然開始埋怨道:“這個社會太不公平了,為什么富人天天悠閑自在,而窮人就應該天…

解決:springcloud 啟動 config-client 報錯:... .integration.config.HandlerMethodArgumentResolversHolder

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 1. springcloud 啟動 config-client 報錯: java.lang.ClassNotFoundException: org.springframework.integration.config.…

美國華裔科學家談免疫系統真相!運動,心情勝過一切藥品!

尋找疾病的真正病因 1928年,抗生素誕生了,我們向世人宣稱能夠控制所有感染性的疾病。但我們沒有想到,抗生素會給人類帶來一系列疾病。過去,醫學人員認為是病毒、細菌制造了疾病。但是現代研究發現,有10%的病…

解決:RabbitMQ 連接報錯:amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 1. springcloud 工程中 config-client 中使用到 RabbitMQ ,啟動工程報錯: org.springframework.amqp.AmqpConnectException…

中國股市下跌的秘密

價值投資似乎是股市投資中的不二法則。 但是價值投資的擁躉者近期遇到無數的疑惑,眾多公司股價跌破凈資產仍然無法止跌,而低至五倍市盈率的銀行股無人問津,盡管監管層越位充當了市場的評論員,公開宣稱藍籌股投資價值不菲&#xff…

springCloud - 第9篇 - 同步配置文件(消息總線方式)

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 一、 微服務系統中有多個服務應用,也會有多個配置文件。此時也可用 springcloud bus 來實現對配置文件的管理。 PS&#xf…

過度擴容 A股“虛胖”

新股擴容每周5到8家,再融資和大小非減持不斷,新三板預計今年推出,國際板開始預熱……擴容“大躍進”已成為A股市場難以承受之重。數據顯示,目前上證指數不到2500點,與最高點相比已跌去了六成;而如果以流通市…