從Rust到遠方:ASM.js星系

2021-02-13 Rust語言中文社區

來源: https://mnt.io/2018/08/28/from-rust-to-beyond-the-asm-js-galaxy/

這篇博客文章是這一系列解釋如何將Rust發射到地球以外的許多星系的文章的一部分:

前奏,

WebAssembly 星系

ASM.js星系(當前這一集)

C 星系

PHP星系,以及

NodeJS 星系

Rust解析器將要探索的第二個星系是ASM.js。這篇文章會解釋什麼是ASM.js,怎樣編譯博客解析器到ASM.js以及如何在瀏覽器中和Javascript一起使用ASM.js. 使用ASM.js的目標是當作WebAssembly不可用的備用方案。我強烈建議你讀讀前一篇關於WebAssembly的文章,因為他們有很多共同的地方

#什麼是ASM.js,為什麼需要ASM.js

Web應用的主要語言是Javascript,任何想要運行在Web上的應用都必須編譯成Javascript,比如遊戲。但是這裡有個問題:編譯輸出文件非常重(即使是WebAssembly),這樣Javascript虛擬機將很難對這樣的代碼做優化,這就導致運行緩慢或者執行低效(可以考慮用遊戲的規模來做例子)。而且在Javascript為編譯目標的情況下,一些語言基礎設施變得毫無用處,比如eval

如果有一種新的語言可以作為編譯目標,而且也能被Javascript虛擬機運行會怎麼樣?這就是WebAssembly,但是在2013年的時候解決方案就是ASM.js:

asm.js, 是一個嚴格的Javascript子集,它能夠被用作編譯器輸出的底層的,高效的目標語言。這個子語言高效的描述>了一個沙盒虛擬機,可以適用於內存不安全的語言,像C或者C++。靜態和動態的組合校驗讓Javascript可以對有效 的asm.js代碼使用一種叫做ahead-of-time(AOT)的編譯時優化策略。

因此ASM.js程序其實只是一個普通的Javascript程序。它不是一個新的語言而是Javascript的一個子集。它可以被任何Javascript虛擬機執行。但是,有個特殊的魔法聲明use asm;,會指示虛擬機用ASM.js引擎來優化這個程序。

ASM.js通過算術運算引入了類型作為標記系統。比如,x|0表示x是一個整數,+x表示x是一個雙精度小數,fround(x)表示x是一個浮點數。下面的例子聲明了一個函數fn increment(x: u32) -> u32:

function increment(x) {
x = x | 0;
return (x + 1) | 0;
}

另一個重要的區別是ASM.js以模塊的方式來運行,以和Javascript隔離。這個模塊是一個需要3個參數的函數:

但是它仍然是Javascript。因此一個好消息是如果你的虛擬機沒有對ASM.js做優化,那麼它運行起來就是普通的Javascript程序,如果有優化,那麼你就可以得到不錯的性能提升。

這個圖片展示了3個基準測試,分別對於不同的Javascript引擎:Firefox, Firefox+asm.js, Google, 和Native。

記住ASM.js是被設計為一種編譯目標。因此一般你不需要特別關心,因為那是編譯器的活。對把C或者C++編譯到Web的一個典型的編譯和執行流程如下

Emscripten,如上圖所示,是一個在這個Web平臺演進過程中非常重要的一個項目。

它是一個用來編譯輸出asm.js和WebAssembly的工具鏈,基於LLVM之上,能夠讓C和C++程序以接近原生應用的速 度運行在Web上,而且不需要任何插件。

如果你和ASM.js或者WebAssembly打交道,遲早有一天你會看到這個名字。I will not explain deeply what ASM.js is with a lot of examples. I recommend instead to read Asm.js: The Javascript Compile Target by John Resig, or Big Web app? Compile it! by Alon Zakai. 我不會用大量的例子來深入的解釋ASM.js.我推薦兩本書:Asm.js: The Javascript Compile Target by John Resig, 或者 Big Web app? Compile it! by Alon Zakai.

我們的過程有點不一樣。我們不會直接編譯Rust代碼到ASM.js,而是先編譯為WebAssembly,然後再編譯為ASM.js。

#Rust 🚀 ASM.js

這個篇章會非常的短,應該說是最簡單的一篇。要編譯Rust到ASM.js你需要先編譯到WebAssembly(參考前一篇文章)然後再編譯WebAssembly二進位到ASM.js。

事實上真正需要ASM.js的地方是那些不支持WebAssembly的瀏覽器,比如IE。它只是我們在Web運行我們程序的一個後備方案。

下面看看這個流程:

wasm2js會是你最好的朋友,它用來編譯你的WebAssembly二進位為ASM.js模塊,它屬於Binaryen項目。下面假設我們已經有了程序的WebAssembly二進位,只需要運行下面的命令:

$ wasm2js --pedantic --output gutenberg_post_parser.asm.js gutenberg_post_parser.wasm

在這一步,gutenberg_post_parser.asm.js 有212kb。這個文件包含ECMAScript 6代碼。注意這裡因為考慮了老瀏覽器如IE,所以代碼需要一點小小的轉換來優化和精簡ASM.js模塊,我們用uglify-es工具,如下:

$
$ sed -i '' '1s/^/function GUTENBERG_POST_PARSER_ASM_MODULE() {/; s/export //' gutenberg_post_parser.asm.js
$ echo 'return { root, alloc, dealloc, memory }; }' >> gutenberg_post_parser.asm.js

$
$ uglifyjs --compress --mangle --output .temp.asm.js gutenberg_post_parser.asm.js
$ mv .temp.asm.js gutenberg_post_parser.asm.js

就像我們優化WebAssembly二進位一樣,我們也可以gzip和brotli壓縮輸出文件:

$
$ gzip --best --stdout gutenberg_post_parser.asm.js > gutenberg_post_parser.asm.js.gz
$ brotli --best --stdout --lgwin=24 gutenberg_post_parser.asm.js > gutenberg_post_parser.asm.js.br

最終我們得到了下面的文件尺寸:

.asm.js: 54kb,

.asm.js.gz: 13kb,

.asm.js.br: 11kb.

都是非常小的!

思考一下,這裡面涉及到了很多的轉換:從Rust到WebAssembly到Javascript/ASM.js。。。工具的數量相對於工作量是非常少的。這展現了一個良好設計的流水線和不同工作組的人的良好合作。

題外話:如果你在讀這篇博客,我假設你是一個開發者。既然如此,我很確定你可以花幾個小時閱讀原始碼就像欣賞大師的畫作。但是你有沒有想過把Rust編譯出來的Javascript輸出是什麼樣的?

我可以說非常的喜歡它!

#ASM.js 🚀 Javascript

輸出的gutenberg_post_parser.asm.js文件包含一個唯一的函數叫做:GUTENBERG_POST_PARSER_ASM_MODULE,它返回一個對象包含了4個私有函數。

root,語法根

alloc,來分配內存

dealloc,釋放內存

memory,內存緩衝區

如果你看過上一篇WebAssembly的文章那麼你會對這些概念感到熟悉。不要指望root會返回一個完整的AST,它只會返回一個內存指針,數據需要進一步編解碼,也需要用同樣的方式對內存進行讀寫。是的,相同的方式。因此邊界層代碼完全是一樣的。你是否還記得在WebAssembly中作為Javascript邊界的Module對象?那和GUTENBERG_POST_PARSER_ASM_MODULE函數返回的完全是一樣的。你甚至可以用這個對象替換。

所有的代碼都在這裡。它完全重用WebAssembly的Javascript邊界層代碼,只是Module有一些不一樣,也沒有加載WebAssembly二進位。結果是ASM.js邊界代碼只用了34行就寫出來了,壓縮後僅有218個字節。

#結論

我們已經看到ASM.js可以在只支持Javascript的環境中(像IE)作為WebAssembly的備用方案,並可適配環境打開或者關閉ASM.js優化。

輸出的ASM.js文件以及其邊界層代碼都非常的小。設計上,ASM.js邊界層代碼重用WebAssembly的Javascript邊界層代碼,因此同樣只有一小部分外部接口代碼需要審查和維護,這非常的有用。

我們已經在前一篇文章中看到Rust很快的速度。我們也已經在比較WebAssembly版本和純Javascript版本解析器中看到同樣速度結論。然而這個結論是否也適用於ASM.js模塊呢?其實在這種情況下ASM.js只是一個備用方案,和其他備用方案一樣通常都比較明顯的慢於目標實現。下面是一個將Rust解析器作為ASM.js模塊運行的基準測試:

testJavascript parser (ms)Rust parser as an ASM.js module (ms)speedupdemo-post.html15.3682.718× 6shortcode-shortcomings.html31.0228.004× 4redesigning-chrome-desktop.html106.41619.223× 6web-at-maximum-fps.html82.9227.197× 3early-adopting-the-future.html119.88038.321× 3pygmalian-raw-html.html349.07523.656× 15moby-dick-parsed.html2,543.75361.423× 7

ASM.js模塊版本的Rust解析器比純Javascript實現平均快6倍。中位數也是6倍。這和WebAssembly版本比較還是有較大差距,但是考慮到這是個備用方案,而且平均已經快了6倍了,已經很不錯了!

因此不僅是整個工作流因為Rust而變得更加安全,而且得到的結果也比Javascript快。

在這個系列的後續文章中我們將會看到Rust會到達很多的星系,Rust越多的往後旅行,也會變得更加有趣。

謝謝閱讀!

相關焦點

  • IntelliJ-Rust —— Rust 語言的 IntelliJ 插件
    具體更新內容如下: Easier linking in rustdoc Rustdoc 是 Rust 發行版中包含的庫文檔工具,可以讓你用 Markdown 編寫文檔。在 1.48.0 版本中,Rustdoc 能夠使用 item 路徑作為連結直接連結到 Markdown 文檔中的其他 rustdoc 頁面。例如,/// Uses [`st...
  • Node.js 2018 用戶調查:最愛 Express,最想學 Rust
    Node.js 基金會近日發布了 2018 Node.js 用戶調查報告,這是該基金會舉報的第三屆年度用戶調查,於 2017 年
  • 腐蝕rust閃退怎麼辦 腐蝕rust程序退出解決方法
    很多玩家在進入腐蝕rust 解決方法 我用的是win10系統 打開任務管理器,在進程裡找到mycolor2 滑鼠右鍵 結束任務再登錄rust 成功解決!
  • ASM啟用世界最大材料科學資料庫 收錄百年文獻 搜索技術簡潔
    【據asminternational網站2020年9月24日報導】ASM國際協會啟用材料數據科學平臺(MPDS),該平臺是世界是上最大和最全面的無機材料資料庫,資料庫收錄了從1900年到現在世界各地的文獻,內容涵蓋材料相關的物理、機械、電氣、光學、磁等多學科內容。
  • Rust 的 2016 年規劃,1.0 之後會如何發展?
    再如 rust-postgres-macros crate,可以在編譯時檢查 SQL 語句的語法。這些 crate 用到了一個編譯器的插件系統,這個插件系統很不穩定,並且暴露出太多的編譯器內部細節。我們計劃提出一個新的插件系統的設計,這個插件系統會更穩定,並且內置了對安全的宏擴展的支持。
  • Rust 1.5來了
    Cargo install已被用在 rustfmt 這樣的工具應用安裝上(rustfmt是用來格式化Rust的代碼半成品),此外還可用於安裝Cargo自身的新子命令上: cargo-check:對項目進行靜態檢查,但不會生成二叉樹/語法樹。
  • Modulus 正式開放 —— Node.js 應用託管平臺
    具體更新內容如下: Easier linking in rustdoc Rustdoc 是 Rust 發行版中包含的庫文檔工具,可以讓你用 Markdown 編寫文檔。在 1.48.0 版本中,Rustdoc 能夠使用 item 路徑作為連結直接連結到 Markdown 文檔中的其他 rustdoc 頁面。例如,/// Uses [`st...
  • 哈勃拍到了「塵土飛揚」的NGC 4036星系!
    其中不乏一些令人驚嘆的圖景,比如近日展示的一個「塵土飛揚」的 NGC 4036 星系。 塵埃是這個星系的標誌性特性,儘管天文學家只能大膽猜測成因,但事情本身還是相當有趣的。NASA 對 NGC 4036 的描述是:該星系以其不規則的塵埃帶而聞名,它們圍繞銀河系中心旋轉,形成了一個螺旋形的圖案。核心周邊是一股延伸的、朦朧的氣體和塵埃。其伸展到了更遠的地方,在這裡產生了溫暖、模糊的光芒。
  • Rust 1.46.0 發布 - Rust - IT之家
    此外,此版本還穩定了兩個新的 API:Option::zipvec::Drain::as_slice更新說明:https://blog.rust-lang.org/2020/08/27/Rust-1.46.0.html
  • 【Rust日報】2020-09-05 微軟在c+靜態分析工具實現了一些rust的...
    微軟在c++靜態分析工具實現了一些rust的安全規則 Rust和c++是兩種流行的系統程式語言。多年來,c++的焦點一直放在性能上。我們越來越多地聽到來自客戶和安全研究人員的呼籲,認為c++語言應該有更強的安全保證。
  • 人類能觀察到幾十億光年外的星系,怎麼不能看到過去一秒的景象?
    所以光年是距離單位不是時間單位,但是,看幾十億光年外的星系的確看到的是該星系幾十億年前的樣子,因為光跑了幾十億年才到達我們地球,被我們看到。人類發展出了一系列觀察遠方星系的技術,從地面上的各種光學天文望遠鏡以及射電天文望遠鏡,到利用火箭發射到太空中的太空望遠鏡。這些技術,可以捕捉到極其微弱的光,讓我們得到遠方星系的照片,分析這些照片,天文學家們就能算出星系的距離。
  • three.js為何如此奇妙
    WebGL是在瀏覽器中實現三維效果的一套規範,而最初使用WebGL原生的API來寫3D程序是一件非常痛苦的事情,在辛苦的付出下WebGL開源框架出現了,其中three.js就是非常優秀的一個,它掩蓋了很多麻煩的細節,那麼,就讓我們一起來看看,什麼是three.js吧!
  • Rust 全新官網已上線測試,這樣的風格你喜歡嗎?
    是的,Rust 那個萬年不變的「極簡主義」風格官網要改版了,目前 beta 版已上線測試,https://beta.rust-lang.org/ —— 大家可以點擊這裡體驗三分鐘
  • total.js 2.4.0 發布,Node.js 的 MVC 框架
    total.js 2.4.0 發布了。total.js 是一個 web 應用框架,使用 JavaScript、HTML、CSS 和 Node.js(MVC) web 應用框架來構建 web 網站和 web 應用。
  • 遠方星系的紅移是否可以超過光速?基於螺旋總時空不會超光速1-3
    這與通常已經觀測到的螺旋星系的特徵相符。現在的觀測結果並不會有嚴重性的觀測問題,問題在於星系的定位。我們需要把觀測到的星系基於地球觀測的現在這個特定時間進行定位。這個定位方式出現了問題。基於螺旋星系、螺旋光線和螺旋光年的定位方式同樣是50億光年發現的星系,基於這種前提,我們該如何在星圖中定位這個星系呢?首先要區分,基於地球這個觀測點,它是在我們的總時空螺旋星系外側,還是在總時空螺旋星系內側的星系?
  • Angular、React 當前,Vue.js 優劣幾何?
    很多時候,人們不會給 Vue.js 足夠的關注。因此,我想分享一些有關 Vue.js 的信息,以及我在使用 Vue.js 來構建生產應用中獲得的一些個人經驗。現在讓我們一起來問這個問題:Vue.js 是適合你的框架嗎?注意:我並不想說 Vue.js 比 React、Angular 或者其它的任何前端框架好。這裡我分享的只是我在項目中使用 Vue.js 而獲得的一些個人體驗。
  • VUE與JS的對比
    我們需要讓所有綁定的對象和元素都能感知到變化1.1.1. vue與js的對比1.1.1.1. js的實現(了解)<!引入js文件--><script src="../node_modules/vue/dist/vue.js"></script></head><body><!--2.
  • Node.js 開發者們都在做什麼?
    同時也告訴我們Node.js開發者面臨著一大痛點:調試。本次調查從七月11日到八月15日總共歷時35天,總共有1126名node.js開發者參與了本次的調查。其中55%的開發者具有兩年以上的node.js開發經驗,26%具有1到兩年的經驗。20%為公開交易的公司工作,7%在500強的公司工作。
  • 手寫動態 3D 蛛網圖 | THREE.js
    https://threejsfundamentals.org/threejs/lessons/threejs-primitives.html又比如,下面演示了如何通過矢量字體,生成三體空間中的形狀。實在不行,可以選擇魔改一把 THREE,再硬嵌入到項目中。這就回到補充基本概念的時候。https://threejsfundamentals.org/threejs/lessons/threejs-primitives.html在 THREE 中,模型是通過 Gemoetry 設定的。