摘要:教程之代碼可移植性與限制一翻譯云荒杯傾本文是專欄系列文章之一,更多文章請(qǐng)查看專欄。下面是正文代碼可移植性與限制幾乎可以編譯任何可移植的代碼到。如果標(biāo)準(zhǔn)機(jī)構(gòu)將共享狀態(tài)添加到中,支持多線程代碼將成為可能。
Emscripten教程之代碼可移植性與限制(一)
翻譯:云荒杯傾
本文是Emscripten-WebAssembly專欄系列文章之一,更多文章請(qǐng)查看專欄。
也可以去作者的博客閱讀文章。
歡迎加入Wasm和emscripten技術(shù)交流群,群聊號(hào)碼:939206522。
Emscripten代碼移植主題涵蓋了將C、C++代碼移植到Emscripten時(shí)需要考慮的所有核心考慮問(wèn)題,以及一般的編碼和調(diào)試指南。
共有以下主題。
每一部分內(nèi)容都比較多,本文主要講第一部分,代碼可移植性與限制。下面是正文:
代碼可移植性與限制Emscripten幾乎可以編譯任何可移植的c/c++代碼到JavaScript。但由于瀏覽器環(huán)境限制和Emscripten編譯出來(lái)的代碼的限制,一些代碼為了能被編譯需要做改動(dòng),本文就幫我們找出這部分代碼。
1、關(guān)于可移植性的指導(dǎo)本節(jié)解釋了哪些類型的代碼是不可移植的(或者更難于移植);哪些代碼可以編譯,但會(huì)運(yùn)行得很慢。開(kāi)發(fā)人員可以使用這些信息來(lái)評(píng)估移植代碼和重寫(xiě)代碼的工作量。
1.1 不能編譯的代碼為了使Emscripten工作,下面類型的代碼需要重寫(xiě)。(理論上,在使用模擬的情況下,可以使用Emscripten解決這些問(wèn)題,但速度非常慢。)
代碼是多線程的,并使用共享狀態(tài)。JavaScript有線程(web workers),但它們不能共享狀態(tài)。它們傳遞消息postMessage()。
Note: 如果JavaScript標(biāo)準(zhǔn)機(jī)構(gòu)將共享狀態(tài)添加到webworker中,支持多線程代碼將成為可能。
代碼依賴于大端序架構(gòu)。Emscripten編譯的代碼目前需要一個(gè)little-endian主機(jī)運(yùn)行,這種主機(jī)占了連接到互聯(lián)網(wǎng)的99%的機(jī)器。這是因?yàn)镴avaScript類型化數(shù)組服從主機(jī)字節(jié)序,LLVM需要知道目標(biāo)字節(jié)序是什么。
依賴于x86對(duì)齊方式的代碼。x86允許未對(duì)齊的內(nèi)存讀寫(xiě)(例如,您可以從一個(gè)非偶數(shù)地址讀取一個(gè)16位的值),但是其他的架構(gòu)不是。對(duì)于emscripten生成的JavaScript,其內(nèi)存對(duì)齊方式是未定義的。如果您使用
SAFE_HEAP = 1構(gòu)建您的代碼,那么您將得到一個(gè)清晰的運(yùn)行時(shí)異常,參見(jiàn)調(diào)試。
使用本機(jī)環(huán)境的底層特性的代碼,例如setjmp / longjmp涉及的本地堆棧操作。(we support proper setjmp/longjmp, i.e.,
jumping down the stack, but not jumping up to an unwound stack, which is undefined behavior).
掃描寄存器或堆棧的代碼。因?yàn)樵诩拇嫫骰蚨褩I系淖兞靠赡苁潜环胖玫揭粋€(gè)并不能被掃描的js局部變量里保存的。
NOTE: 如果你是一個(gè)喜歡自己寫(xiě)垃圾回收程序的程序員,可能對(duì)這類代碼比較熟。。。
具有特定于體系結(jié)構(gòu)的內(nèi)聯(lián)匯編(比如包含x86代碼的asm())的代碼是不可移植的。這段代碼需要用可移植的C或C++來(lái)替換。有時(shí),代碼庫(kù)會(huì)將可移植的代碼和可選的內(nèi)聯(lián)程序集寫(xiě)在一起作為優(yōu)化,你需要找到一個(gè)選項(xiàng)使內(nèi)聯(lián)匯編代碼不可用。
1.2 能編譯但是運(yùn)行得比較慢的代碼Note: 當(dāng)你要優(yōu)化代碼的時(shí)候,就會(huì)知道了解這些事項(xiàng)是有用的。
下面類型的代碼會(huì)被編譯,但是可能運(yùn)行的很慢:
64位整型變量。數(shù)學(xué)運(yùn)算(+,-,*,/)是慢的,因?yàn)樗鼈兪潜荒M的。這是因?yàn)镴avaScript沒(méi)有本地64位int類型,因此這是不可避免的。
C++異常。在JavaScript中,這些代碼通常會(huì)使JavaScript引擎關(guān)閉各種優(yōu)化。因此,在- o1和上面的默認(rèn)情況下,異常會(huì)被關(guān)閉。要重新啟用它們,請(qǐng)運(yùn)行emcc與- s DISABLE_EXCEPTION_CATCHING= 0。
setjmp also prevents relooping around it,迫使我們使用一種效率較低的方法來(lái)模擬控制流。
2、API限制瀏覽器環(huán)境和JavaScript不同于C/C++通常運(yùn)行的本地環(huán)境。這些差異對(duì)如何調(diào)用和使用本地API施加了一些限制。本部分列出了一些比較明顯的限制。
2.1 網(wǎng)絡(luò)Emscripten支持libc庫(kù)的網(wǎng)絡(luò)函數(shù),但您必須限制他們是異步(非阻塞)操作。這是因?yàn)榈讓拥腏avaScript網(wǎng)絡(luò)函數(shù)是異步的。
2.2 文件系統(tǒng)Emscripten支持libc文件系統(tǒng)函數(shù),C /C++代碼可以以正常方式編寫(xiě)。
在瀏覽器環(huán)境中運(yùn)行的代碼是沙盒sandboxed,并且不直接訪問(wèn)本地文件系統(tǒng)。然后,Emscripten就創(chuàng)建了一個(gè)虛擬文件系統(tǒng),它可以預(yù)裝數(shù)據(jù),或者鏈接到url來(lái)懶加載。這會(huì)影響同步文件系統(tǒng)函數(shù)調(diào)用以及一個(gè)項(xiàng)目如何被編譯。關(guān)于這方面,請(qǐng)參見(jiàn)文件系統(tǒng)概述。
2.3 主函數(shù)死循環(huán)瀏覽器事件模型使用合作模式的多任務(wù)處理——每個(gè)事件都有一個(gè)運(yùn)行的“turn”,然后必須將控制權(quán)返回給瀏覽器事件循環(huán),這樣其他事件就可以處理了。
HTML頁(yè)面掛起的一個(gè)常見(jiàn)原因是JavaScript未完成并且未將控制權(quán)返回給瀏覽器。
這將影響含有死循環(huán)的主函數(shù)的代碼編寫(xiě)。有關(guān)更多信息,請(qǐng)參見(jiàn)Emscripten Runtime環(huán)境。
3、函數(shù)指針的問(wèn)題函數(shù)指針有三個(gè)主要的問(wèn)題:
1、指針類型轉(zhuǎn)換會(huì)引起指針調(diào)用失敗。
針對(duì)函數(shù)聲明時(shí)的簽名不同,函數(shù)指針會(huì)被存儲(chǔ)到不同的表中。當(dāng)一個(gè)函數(shù)被調(diào)用時(shí),它會(huì)在與當(dāng)前函數(shù)指針簽名關(guān)聯(lián)的表中搜索它。如果你進(jìn)行了指針類型轉(zhuǎn)換,而指針和所有的表并沒(méi)有被修改,則調(diào)用代碼將在錯(cuò)誤的表中查找。而錯(cuò)誤的表中實(shí)際上很可能并沒(méi)有一個(gè)叫該名字的指針,這樣就出錯(cuò)了。
例如,一個(gè)聲明為int(int)(返回int,接收int)的函數(shù),會(huì)被添加到表FUNCTION_TABLE_ii。如果您將一個(gè)指向該函數(shù)指針投射到void(int)(不返回,接收int),那么代碼將在FUNCTION_TABLE_vi中查找函數(shù)。
你可能看到編譯警告:
warning: implicit declaration of function
推薦的解決方案是重構(gòu)代碼以避免這種情況,如下面的Asm指針轉(zhuǎn)換所描述的那樣。
2、當(dāng)你使用-o2以及更高優(yōu)化級(jí)別的時(shí)候,比較不同類型的函數(shù)指針會(huì)產(chǎn)生錯(cuò)誤的結(jié)果,而錯(cuò)誤的函數(shù)指針可能更具誤導(dǎo)性。要檢查你的代碼出問(wèn)題的原因,可以將aliasing_function_pointer設(shè)為零,(- s aliasing_function_pointer= 0)進(jìn)行編譯。
NOTE: 在asm.js中,函數(shù)指針存儲(chǔ)在特定函數(shù)類型的表中。如FUNCTION_TABLE_ii。 在較低級(jí)別的優(yōu)化中,每個(gè)函數(shù)指針在所有函數(shù)類型表上都有一個(gè)惟一的索引值(一個(gè)函數(shù)指針只在其中一個(gè)表的某個(gè)索引位置存在,在所有其他表中 這個(gè)索引位置都是一個(gè)空槽)。因此,比較函數(shù)指針(索引)能給出了一個(gè)準(zhǔn)確的結(jié)果,但如果是試圖在錯(cuò)誤的表中調(diào)用函數(shù)指針,將會(huì)拋出一個(gè)錯(cuò)誤,因?yàn)樵撍饕强盏摹? 在-o2和更高級(jí)別的優(yōu)化設(shè)置下,表被優(yōu)化,以至于所有函數(shù)指針都在順序索引中。這是一個(gè)有用的優(yōu)化,因?yàn)槿绻麤](méi)有所有空槽,表就更緊湊, 但它確實(shí)意味著函數(shù)索引不再是“全局”的惟一(因?yàn)橐粋€(gè)函數(shù)指針在這張表中的索引位置與在另一張表中的索引位置不同了)。此時(shí)需要一張?zhí)囟ǖ谋?和在這樣表中的特定位置索引才能夠唯一索引到一個(gè)函數(shù)。 因此,高級(jí)別的優(yōu)化編譯: 1、由于不同類型的函數(shù)可以有相同的索引(盡管在不同的表中),函數(shù)指針的比較可能會(huì)產(chǎn)生錯(cuò)誤的結(jié)果。 2、函數(shù)指針代碼中的錯(cuò)誤更難于調(diào)試,因?yàn)樗鼈儗?dǎo)致錯(cuò)誤的代碼被調(diào)用,而不是顯式的錯(cuò)誤(就像在表中的“漏洞”中那樣)。
3、結(jié)構(gòu)體按值傳遞時(shí),老版本的clang會(huì)為c和c++代碼生成兩種不同的代碼,這兩種格式的代碼不兼容,你可能會(huì)收到一個(gè)警告。
解決方案是按引用傳遞結(jié)構(gòu)體,或者不要在有結(jié)構(gòu)體的位置混淆c和c++(比如,重命名.c為.cpp)。
如上所述,在asm.js模式下,函數(shù)指針必須使用正確的類型調(diào)用,否則調(diào)用將失敗。這是因?yàn)樵诤瘮?shù)聲明的時(shí)候,每個(gè)函數(shù)指針會(huì)基于這個(gè)函數(shù)的簽名被存儲(chǔ)在一個(gè)特定的表中: 將指針轉(zhuǎn)換為另一個(gè)類型會(huì)導(dǎo)致調(diào)用代碼在錯(cuò)誤的位置(表)查找函數(shù)指針。
NOTE: 對(duì)于每種類型的函數(shù)指針都有一個(gè)多帶帶的表,可以讓JavaScript引擎知道每個(gè)函數(shù)指針調(diào)用的確切類型,這樣也好進(jìn)而優(yōu)化他們。
有三種解決辦法,優(yōu)先選擇第二種:
調(diào)用者在函數(shù)指針被調(diào)用之前把指針類型再轉(zhuǎn)回原來(lái)的指針類型,這是有問(wèn)題的因?yàn)檫@需要調(diào)用者知道它原來(lái)的類型是什么,而調(diào)用者實(shí)際上并不知道一個(gè)指針之前的類型是什么。
創(chuàng)建一個(gè)不需要轉(zhuǎn)換的適配器函數(shù),從適配器函數(shù)調(diào)用原始函數(shù)。
使用EMULATE_FUNCTION_POINTER_CASTS。當(dāng)你使用-s EMULATE_FUNCTION_POINTER_CASTS=1編譯時(shí),Emscripten將發(fā)出代碼來(lái)模擬運(yùn)行時(shí)的函數(shù)指針類型轉(zhuǎn)換,
添加額外的參數(shù)/刪除參數(shù)/更改參數(shù)類型/添加或刪除返回類型等。這可以增加顯著的運(yùn)行時(shí)開(kāi)銷(xiāo),因此不推薦,但值得嘗試。
4、特定瀏覽器限制本頁(yè)面列出了一些 與Emscripten編譯出來(lái)的應(yīng)用程序和游戲相關(guān)的 主要瀏覽器的最新版本之間的差異:
函數(shù)emscripten_get_now()以毫秒的形式返回一個(gè)wallclock time。
Opera 12.16和Windows谷歌Chrome 28.0.1500.95有一個(gè)限制,即計(jì)時(shí)器的精度僅為毫秒。
在其他主流瀏覽器上(IE10,firefox22,非windows的Chrome 28),也都是亞毫秒精度。
WebGL并沒(méi)有在Internet Explorer得到完全支持(至少在IE12之前)。
Opera 12.16對(duì)W3C的File API的支持有限。特別是它不支持createObjectURL函數(shù),
這意味著不可能使用瀏覽器的圖像編解碼器來(lái)解碼Emscripten虛擬文件系統(tǒng)中的預(yù)加載文件。
Emscripten中OpenAL和SDL audio的支持依賴于Web Audio API。
Emscripten代碼移植系列文章Emscripten代碼移植主題系列文章是emscripten中文站點(diǎn)的一部分內(nèi)容。
第一個(gè)主題介紹代碼可移植性與限制
第二個(gè)主題介紹Emscripten的運(yùn)行時(shí)環(huán)境
第三個(gè)主題第一篇文章介紹連接C++和JavaScript
第三個(gè)主題第二篇文章介紹embind
第四個(gè)主題介紹文件和文件系統(tǒng)
第六個(gè)主題介紹Emscripten如何調(diào)試代碼
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91865.html
摘要:優(yōu)化項(xiàng)也會(huì)引發(fā)一些問(wèn)題。檢查你的代碼是否工作并修復(fù)問(wèn)題。從起,及以上的優(yōu)化級(jí)別默認(rèn)啟動(dòng)了這項(xiàng)設(shè)置。目前正在進(jìn)行改進(jìn)。代碼移植系列文章代碼移植主題系列文章是中文站點(diǎn)的一部分內(nèi)容。 作者:云荒杯傾歡迎加入Wasm和emscripten技術(shù)交流群,群聊號(hào)碼:939206522。 這是關(guān)于Emscripten的系列文章,更多文章請(qǐng)看下面鏈接。 Emscripten代碼移植系列文章 Emscr...
摘要:優(yōu)化項(xiàng)也會(huì)引發(fā)一些問(wèn)題。檢查你的代碼是否工作并修復(fù)問(wèn)題。從起,及以上的優(yōu)化級(jí)別默認(rèn)啟動(dòng)了這項(xiàng)設(shè)置。目前正在進(jìn)行改進(jìn)。代碼移植系列文章代碼移植主題系列文章是中文站點(diǎn)的一部分內(nèi)容。 作者:云荒杯傾歡迎加入Wasm和emscripten技術(shù)交流群,群聊號(hào)碼:939206522。 這是關(guān)于Emscripten的系列文章,更多文章請(qǐng)看下面鏈接。 Emscripten代碼移植系列文章 Emscr...
摘要:運(yùn)行時(shí)環(huán)境與大多數(shù)應(yīng)用程序所期望的環(huán)境不同。不過(guò)程序是要手動(dòng)交換緩沖區(qū)的。第一個(gè)主題介紹代碼可移植性與限制第二個(gè)主題介紹的運(yùn)行時(shí)環(huán)境第三個(gè)主題第一篇文章介紹連接和第三個(gè)主題第二篇文章介紹第四個(gè)主題介紹文件和文件系統(tǒng)第六個(gè)主題介紹如何調(diào)試代碼 翻譯:云荒杯傾本文是Emscripten-WebAssembly專欄系列文章之一,更多文章請(qǐng)查看專欄。也可以去作者的博客閱讀文章。 Emscrip...
閱讀 3591·2021-11-04 16:06
閱讀 3589·2021-09-09 11:56
閱讀 854·2021-09-01 11:39
閱讀 906·2019-08-29 15:28
閱讀 2300·2019-08-29 15:18
閱讀 837·2019-08-29 13:26
閱讀 3338·2019-08-29 13:22
閱讀 1051·2019-08-29 12:18