当前位置: 首页 > Web前端 > HTML5

Emscripten教程的代码可移植性和局限性(一)

时间:2023-04-05 21:29:51 HTML5

Emscripten教程的代码可移植性和局限性(一)译文:浮云与苍凉杯本文为Emscripten-WebAssembly专栏系列之一。更多文章请查看专栏。也可以去作者的博客阅读文章。欢迎加入Wasm和emscripten技术交流群,群聊号:939206522。Emscripten代码移植主题涵盖了将C、C++代码移植到Emscripten时需要考虑的所有核心注意事项,以及通用的编码和调试指南。有以下主题。代码可移植性和局限性、emscripten的运行环境、连接C++和JavaScript、文件和文件系统、多媒体数据和图形、调试、多线程编程支持、移植SIMD代码、Asyncify、Emterpreter每一部分内容比较多,本文主要讲一下第一部分,代码的可移植性和局限性。文本如下:代码可移植性和限制Emscripten可以将几乎所有可移植的c/c++代码编译为JavaScript。但是由于浏览器环境和Emscripten编译的代码的限制,需要改动一些代码才能编译通过。本文将帮助我们找到这部分代码。1.可移植性指南本节解释哪些类型的代码不可移植(或更难移植);哪些代码可以编译但运行速度很慢。开发人员可以使用此信息来估计移植和重写代码的工作量。1.1无法编译的代码为了使Emscripten工作,需要重写以下类型的代码。(理论上在使用mocks的时候可以使用Emscripten来解决这些问题,但是速度很慢。)代码是多线程的,使用共享状态。JavaScript有线程(网络工作者),但它们不能共享状态。他们传递消息postMessage()。注意:如果JavaScript标准主体将共享状态添加到webworker,将有可能支持多线程代码。该代码依赖于大端架构。Emscripten编译的代码目前需要小端主机运行,占所有联网机器的99%。这是因为JavaScript类型数组遵循主机字节序,而LLVM需要知道目标字节序是什么。依赖于x86对齐的代码。x86允许未对齐的内存读取和写入(例如,您可以从非偶数地址读取16位值),但其他体系结构则不允许。对于emscripten生成的JavaScript,它的内存对齐是未定义的。如果您使用SAFE_HEAP=1构建代码,那么您将得到一个明确的运行时异常,请参阅调试。使用本机环境低级特性的代码,例如setjmp/longjmp中涉及的本机堆栈操作。(我们支持适当的setjmp/longjmp,即跳下堆栈,但不跳到展开的堆栈,这是未定义的行为)。扫描寄存器或堆栈的代码。因为寄存器或栈上的变量可能存放在无法扫描到的js局部变量中。注意:如果你是一个喜欢自己编写垃圾收集程序的程序员,你可能对这种代码很熟悉。..具有特定体系结构内联汇编的代码(如包含x86代码的asm())不可移植。此代码需要替换为可移植的C或C++。有时代码库将可移植代码与可选的内联汇编一起编写作为优化,您需要找到一个选项来禁用内联汇编代码。1.2编译通过但运行速度较慢的代码注意:当你想优化代码时,你就会知道了解这些东西是很有用的。以下类型的代码可以编译,但运行速度可能会很慢:64位整数变量。数学运算(+、-、*、/)很慢,因为它们是模拟的。这是因为JavaScript没有原生的64位int类型,所以这是不可避免的。C++异常。在JavaScript中,这些代码通常会导致JavaScript引擎关闭各种优化。因此,默认情况下,在-o1及更高版本中,异常是关闭的。要重新启用它们,请使用-sDISABLE_EXCEPTION_CATCHING=0运行emcc。setjmp还可以防止围绕它重新循环,迫使我们使用一种效率较低的方式来模拟控制流。2.API限制浏览器环境和JavaScript不同于C/C++通常运行的本地环境。这些差异对如何调用和使用本机API施加了一些限制。本节列出了一些更明显的限制。2.1网络Emscripten支持libc库的网络功能,但你必须将它们限制为异步(非阻塞)操作。这是因为底层JavaScript网络函数是异步的。2.2文件系统Emscripten支持libc文件系统函数,可以正常编写C/C++代码。在浏览器环境中运行的代码是沙箱化的,不会直接访问本地文件系统。Emscripten然后创建一个虚拟文件系统,可以预加载数据,或链接到url以进行延迟加载。这会影响同步文件系统函数调用以及项目的编译方式。在这方面,请参阅文件系统概述。2.3MainFunctionInfiniteLoop浏览器事件模型使用协作式多任务——每个事件都有一个运行的“回合”,然后必须将控制返回给浏览器事件循环,这样其他事件才能被处理。HTML页面挂起的一个常见原因是JavaScript未完成且未将控制权返回给浏览器。这样会影响死循环的main函数的代码写法。有关详细信息,请参阅Emscripten运行时环境。3、函数指针的问题函数指针主要存在三个问题:1、指针类型转换会导致指针调用失败。根据函数声明的签名,函数指针将存储在不同的表中。当一个函数被调用时,它会在与当前函数指针签名关联的表中搜索它。如果你进行指针转换,并且指针和所有表都没有被修改,调用代码将查找错误的表。而错误的表可能实际上没有具有该名称的指针,所以出了问题。例如,声明为int(int)(返回int,接收int)的函数将被添加到表FUNCTION_TABLE_ii中。如果将指向该函数的指针转换为void(int)(不返回,接收int),则代码将在FUNCTION_TABLE_vi中查找该函数。您可能会看到编译警告:warning:implicitdeclarationoffunction推荐的解决方案是重构代码以避免这种情况,如下面的Asm指针转换中所述。2.当你使用-o2和更高的优化级别时,比较不同类型的函数指针会产生错误的结果,而且错误的函数指针可能更具有误导性。要检查您的代码有什么问题,请将aliasing_function_pointer设置为零(-saliasing_function_pointer=0)进行编译。注意:在asm.js中,函数指针存储在特定函数类型的表中。比如FUNCTION_TABLE_ii。在较低的优化层次上,每个函数指针在所有函数类型的表上都有唯一的索引值(一个函数指针只存在于其中一个表中的某个索引位置,而这个索引位置在所有其他表中都是一个空槽).因此,比较函数指针(索引)会给出准确的结果,但尝试调用错误表中的函数指针会抛出错误,因为索引为空。在-o2和更高的优化设置下,表被优化,使得所有函数指针都在顺序索引中。这是一个有用的优化,因为没有所有空槽的表更紧凑,但这确实意味着函数索引不再是“全局”唯一的(因为函数指针在这个表中具有与在另一个索引中相同的索引位置立场不同)。这时候就需要一个特定的表,以及在这个表中的特定位置索引来唯一索引一个函数。因此,高级优化编译:1.由于不同类型的函数可以有相同的索引(虽然在不同的表中),函数指针的比较可能会产生错误的结果。2.函数指针代码中的错误更难调试,因为它们会导致调用错误的代码,而不是显式错误(如表中的“漏洞”)。3.结构传值时,老版本的clang会生成c和c++代码两种不同的代码。这两种格式的代码不兼容,您可能会收到警告。解决方案是通过引用传递结构,或者在有结构的地方不要混淆c和c++(例如,将.c重命名为.cpp)。asm指针转换上面提到,在asm.js模式下,调用函数指针必须使用正确的类型,否则会调用失败。这是因为在函数声明时,每个函数指针都存储在基于函数签名的特定表中:将指针强制转换为另一种类型会导致调用代码在错误的位置(表)中查找函数指针。注意:为每种类型的函数指针创建一个单独的表允许JavaScript引擎知道每个函数指针调用的确切类型,因此它可以优化它们。解决方案有3种,首选第二种:调用者在调用函数指针之前,将指针类型转换回原来的指针类型,这是有问题的,因为它需要调用者知道它原来的类型是什么,而调用者的读者实际上并不知道以前的指针类型是什么。创建一个不需要转换的适配器函数,并从适配器函数中调用原始函数。使用EMULATE_FUNCTION_POINTER_CASTS。当您使用-sEMULATE_FUNCTION_POINTER_CASTS=1进行编译时,Emscripten将发出代码以在运行时模拟函数指针转换,添加额外参数/删除参数/更改参数类型/添加或删除返回类型等。这会增加大量运行时开销,因此不推荐,但值得一试。4.特定浏览器限制本页列出了与Emscripten编译的应用程序和游戏相关的一些主要浏览器的最新版本之间的差异:函数emscripten_get_now()返回以毫秒为单位的挂钟时间。Opera12.16和WindowsGoogleChrome28.0.1500.95有一个限制,即计时器的精度只有毫秒。在其他主流浏览器(IE10、firefox22、非windowsChrome28)上,也是亚毫秒精度。InternetExplorer不完全支持WebGL(至少在IE12之前)。Opera12.16对W3C的文件API的支持有限。特别是它不支持createObjectURL函数,这意味着无法使用浏览器的图像编解码器来解码Emscripten虚拟文件系统中的预加载文件。Emscripten中对OpenAL和SDL音频的支持依赖于WebAudioAPI。Emscripten代码移植系列文章Emscripten代码移植主题系列是emscripten中文站点的一部分。第一篇介绍代码的可移植性和局限性第二篇介绍Emscripten的运行环境第三篇第一篇介绍连接C++和JavaScript第三篇第二篇介绍embind第四篇介绍文件和文件系统第六篇介绍Emscripten如何调试代码