生活工程体验信仰哲学精神
投稿投诉
精神世界
探索历史
哲学文学
艺术价值
信仰创造
境界审美
体验技术
技能工具
工程信息
医学生产
生活运用
操作能力

最强nodejs下C绑定方案介绍

11月11日 枯心人投稿
  作者:john
  最近基于puerts做了个nodejsaddon,能让nodejs方便的调用c的库。拿一个比较知名的同类方案v8pp做对比:
  相同点
  都是基于C模板技术提供了声明式绑定API。
  都能支持nodejs和其它v8环境
  先列几个不同点
  v8pp提供了包括v8的初始化,设置,cjs交互等封装,而puerts仅仅专注于cjs交互一项。
  声明要绑定capi后,puerts能生成这些capi的TypeScript声明(。d。ts文件),这似乎是首创
  puerts对c特性支持丰富些,比如支持函数重载
  puerts的性能更强悍:简单C静态方法比v8pp快5090,简单C成员方法比v8pp快45倍,在此基础上如果开启v8fastapicall特性还能再提升一倍。
  语言无关的原生addon标准
  puerts不仅仅想做更好的v8C绑定方案,还通过“跨语言交互”抽象出来的一套api,定义了一个语言无关的原生addon标准。该标准的addon无需重新编译可以在实现了该标准的游戏引擎(UEUnity),nodejs、lua等环境加载使用。可以下载这个工程体验一下:puertsaddondemos,也期待该标准的更多语言支持。
  反观nodejs原生addon,要在同出一源的electron加载也要用electron的工具重新构建:usingnativenodemodules
  HelloWorld
  被调用的C代码
  classHelloWorld{public:HelloWorld(intp){F}voidFoo(std::functioncmp){boolretcmp(Field,StaticField);std::coutFoo,Field:Field,StaticField:StaticField,compareresult:retstd::}staticintBar(std::stringstr){std::coutBar,str:strstd::returnStaticField1;}intFstaticintStaticF};intHelloWorld::StaticField0;
  声明式导出到addon
  UsingCppType(HelloWorld);voidInit(){puerts::DefineClass()。Constructor()。Method(Foo,MakeFunction(HelloWorld::Foo))。Function(Bar,MakeFunction(HelloWorld::Bar))。Property(Field,MakeProperty(HelloWorld::Field))。Variable(StaticField,MakeVariable(HelloWorld::StaticField))。Register();}helloworldismodulename,willuseinjslater。PESAPIMODULE(helloworld,Init)
  js调用该addon
  constpuertsrequire(puerts);lethelloworldpuerts。load(pathtohelloworld);constHelloWorldhelloworld。HelloWconstobjnewHelloWorld(101);obj。Foo((x,y)xy);HelloWorld。Bar(hello);HelloWorld。StaticField999;obj。Field888;obj。Foo((x,y)xy);
  lua调用该addon
  localpuertsrequirepuertslocalhelloworldpuerts。load(pathtohelloworld)localHelloWorldhelloworld。HelloWorldlocalobjHelloWorld(101)obj:Foo(function(x,y)returnxyend)HelloWorld。Bar(hello)HelloWorld。StaticField999obj。Field888obj:Foo(function(x,y)returnxyend)
  代码解释
  被调用的代码包含了比较常用的几种情况:构造函数、成员变量、成员函数、静态变量、静态函数,也包含了比较高级点的std::function,这种变量在jslua可以直接传函数
  绑定声明部分可以理解为基于c构造的一个dsl,根据文档学习怎么使用即可。
  TypeScript调用代码
  编译好addon后,可以用puerts提供的工具生成声明文件。
  先安装puerts工具
  npminstallgpuerts
  将声明文件生成到typing目录
  puertsgendtspathtoyouraddonttyping
  打开声明文件typingmodulenameindex。d。ts,可以看到针对声明的C类的ts声明:
  declaremodulehelloworld{import{Ref,Nullable,cstring}frompuertsclassHelloWorld{constructor(p0:number);Field:staticStaticField:staticBar(p0:string):Foo(p0:(p0:number,p1:number)boolean):}}
  把typing目录加到ts工程的tsconfig。json的compilerOptionstypeRoots即可享受代码提示、检查之乐。
  上面js调用代码的ts版本如下:
  import{load}importasHelloWorldModluefromhelloworldlethelloworldload(pathtohelloworld);constHelloWorldhelloworld。HelloWconstobjnewHelloWorld(101);obj。Foo((x,y)xy);HelloWorld。Bar(hello);HelloWorld。StaticField999;obj。Field888;obj。Foo((x,y)xy);
  通过HelloWorld例子我们初步了解了puertsfornode的初步使用,想进一步使用请看文档和例子。
  接下来我们讲下设计、实现相关的东东。篇幅的关系只讲两个主题:
  语言无关addon设计
  性能
  语言无关addon设计
  笔者从xLua到puerts,使用过脚本引擎虚拟机有:lua、v8、jscore、quickjs、wasm3等等,感觉脚本引擎虚拟机和宿主交互来来去去就那么回事,于是萌生了一个“做一套跨虚拟机的FFI抽象”的想法。
  C还是C?
  这些引擎有的提供的是C接口,有的提供的是C接口,这抽象接口用哪个语言好?
  很显然应该用C,它兼容性更好,有可能有些环境只能用C,而且一个动态库和可执行程序之间的接口如果用到了C的类型(std::string,std::sharedptr等),两边使用的C版本不一样很容易导致崩溃,如果这些不能用,为何不直接用C?
  回调签名
  虚拟机调用宿主的一个函数,其实是调用宿主注册的一个特定接口的回调,回调中读取参数调用实际函数后,把结果返回给虚拟机。每个虚拟机对这回调的定义基本都不一样,也很难评个高下。最终定了如下回调签名。
  typedefvoid(pesapicallback)(pesapicallbackinfoinfo);
  主要是基于两点考虑:
  这签名和puerts主打支持的v8是兼容的,可以直接作为v8的回调,减少v8适配的性能损失
  单参数的接口,其它多参数回调只要栈上构造一个栈结构体装一下即可,性能损失也不大,以quickjs为例,它的签名是这样的
  typedefJSValueJSCFunctionData(JSContextctx,JSValueConstthisval,intargc,JSValueConstargv,intmagic,JSValuefuncdata);
  虽然差别很大:有很多参数,而且有返回值。我们可以这么适配一下
  structpesapicallbackinfo{JSCJSValueCJSValueCJSVJSV};〔〕(JSContextctx,JSValueConstthisval,intargc,JSValueConstargv,intmagic,JSValuefuncdata){pesapicallbackinfocallbackIcallbackInfo。callbackInfo。callbackInfo。callbackInfo。callbackInfo。callbackInfo。pesapicallbackcallback(pesapicallback)(JSVALUEGETPTR(funcdata〔0〕));callback(callbackInfo);returncallbackInfo。}
  其它接口
  基本数据类型转换
  对象生命周期管理:由虚拟机主动new的原生对象,没引用(gc)时应该释放掉,原生持有的一些虚拟机gc对象,比如回调函数,应该保持引用
  面向对象信息描述:有哪些类,类的函数和成员信息,这些类间的继承关系
  addon初始化
  翻到前面的HelloWorld例子,有这么一行:
  PESAPIMODULE(helloworld,Init)
  PESAPIMODULE是一个宏,这将会在addon动态库中定义几个入口,其中最重要是一个addon初始化函数,实现了“跨虚拟机的抽象接口”的程序加载addon后会主动调用,传入前面说的那一系列接口实现函数的指针。
  pesapi
  前面说的“跨虚拟机的抽象接口”叫pesapi,是PortableEmbeddedScriptingAPI的缩写,整套API的描述只有一个200多行的简单纯c头文件。
  纯用这套api去编写addon也是可以的,这种方式仅仅依赖一个头文件和一个c文件,不依赖任何库。这是一个例子:tinyc
  可以看到比较繁琐,前面的HelloWorld使用的声明式绑定方式简单很多,也仅仅多依赖些头文件和C14,不需要依赖node或者v8。
  性能
  我们对一个C类进行声明式绑定,默认编译后生成的是对pesapi的调用,好处是这种addon不依赖于任何的脚本引擎虚拟机,以二进制形式发布,可以在任意支持pesapi的环境使用,但它也有缺点:脚本引擎虚拟机的API先封装成pesapi再被addon调用,性能会有一些损失。
  具体可以看这个对比测试工程:puertsnodeperformance,主页有多个平台的测试结果,其中puertsperf即为模板绑定pesapi的测试,作为对比的v8apiperf则是手工调用v8api的测试,还是有不小的性能损失的。
  napiperf是手工调用nodejs的napi实现的addon,napi和pesapi类似,都是封装成c接口给addon调用(ps:pesapi的设计也有参考napi),它的测试数据和puerts模板绑定pesapi是差不多的,可见性能损失更多的源于c接口的封装。
  v8API直调优化
  代码不需要修改,只需编译时加入PESEXTENSIONWITHV8API宏即可获得相当大的性能提升,顾名思义加了这个宏,模板将改为调用v8api而不是pesapi,puertsv8perf即是这种方式编译的addon,性能比较接近v8apiperf,远比同样是模板v8api的v8pp性能要好(v8pppref)。
  当然,也有代价的,这导致v8api的依赖,addon编译需要加入v8,而且这种addon也不能在其它虚拟机上跑。
  v8fastapicall支持
  v8有一个甚少人知道和使用的特性:fastapicall。
  前面也说过原生调用是通过特定形式的回调来实现,每一个参数处理都至少有一次函数调用,而fastapicall是根据函数签名信息,用TurboFan编译器运行时jit生成代码完成虚拟机内部CallingConvention到原生CallingConvention的转换,可能一个参数只需要简单的一个指令。
  这特性也有一些坑:
  该特性并不是所有类型都支持,对于不支持的类型,含不支持类型的函数你用它提供给的模板库去收集签名信息时会报编译错误
  成员方法并不直接支持
  碰到过一个神奇的问题:静态方法甚至比不用该特性还慢,进一步摸索发现静态方法先用变量持有再调用就有效果
  constAddCalc。AddAdd(1,2)fastCalc。Add(1,2)veryslow
  网络甚少fastapicall的资料,只能结合源码去摸索去解决这些问题,所幸都搞定了。
  之前puertsv8perf不需要修改代码,只需:
  编译时添加WITHV8FASTCALL宏
  如果是用nodegyp编译,会报找不到v8fastapicalls。h,需要自行下载合适版本的该文件,puertsnodeperformance主页有介绍方法
  启动node要加turbofastapicalls参数
  即可享用这巨大的性能提升。实测puertsfastcallperf比v8apiperf还要快12倍。
投诉 评论 转载

人民日报每日文摘人生的本质就是一个人活着热文聚热点网 人生的本质就是一个人活着不要对别人心存太多期待,我们总是想要找到能为自己分担痛苦和悲伤的人,可太多时候,我们那些惊天动地的伤痛,在别人眼里,不过是随手拂过的尘埃。或……农村修公路房子可以强拆吗?须符合这些条件热闻聚热点网 现在交通越来越发达,人们的出行工具。的选择方式越来越多样,越来越方便。我国主要的出行工具有飞机、高铁、动车、火车、高速公路以及国道。并且随着我国现在经济的迅速发展。在今年我们就……世界摄影大会丽水开幕热文聚热点网 【记者王辉丹综合报导】由浙江省丽水市人民政府与国际摄影艺术联合会共同合作举办的首届世界摄影大会,二二二世界摄影大会昨(十七)日上午开幕。大会开启丽水打造重大国际摄影文化交流平台……数字化全球化:海外网红对品牌营销的影响!热评聚热点网 随着信息技术的飞速发展和全球互联网的普及,数字化全球化已经成为21世纪全球经济的一大趋势。在这个数字时代,社交媒体已经成为人们生活中不可或缺的一部分,而海外网红作为社交媒体平台……“太阳科比”降世!布克32分独领风骚艾顿替身好用10倍热传聚 保罗盘活了勇士全队的进攻,可是太阳的进攻火力实在是过于强大,尤其是布克在关键时刻的执行力,更是帮助太阳笑到了最后。除去布克,努尔基奇在关键时刻的执行力也帮助太阳赢得了最终胜利。……200亿美元的菜鸟国际化故事,香港市场会买单吗?热议聚热点网 虽然菜鸟很乐意将自己塑造为一家高科技公司,但它与联邦快递、UPS以及顺丰并没有本质区别。9月26日,菜鸟网络向港交所递交上市计划书,成为阿里巴巴实施“16N”变革之后首个……最强nodejs下C绑定方案介绍热博聚热点网 作者:john最近基于puerts做了个nodejsaddon,能让nodejs方便的调用c的库。拿一个比较知名的同类方案v8pp做对比:相同点都是基于C模板……为何以前的猪肉不粘锅?而现在的猪肉一炒就粘锅,告诉你3个原因 如今人们的生活水平越来越好,吃的穿的都不用愁了,相比较物资匮乏的年代,真的过得舒服又惬意。可是大家有没有发现,很多人茶余饭后总是很怀念以前的生活,虽然简单清贫,但却过的很快乐。……你觉得《刀剑神域》算是“神作”吗?热传聚热点网 谢邀,不算。我非常喜欢刀剑,小说和动漫通吃,但如果说刀剑是神作那无疑是在捧杀。要说人气,除开民工漫和史诗级的TV,《刀剑神域》绝对能排进前列。《刀剑神域》轻小……做外贸与客户谈判,掌握主动权很重要热传聚热点网 当客户跟你说:我考虑考虑,再联系你。你一般会怎么提问,能够让客户与您分享更多?好的,有需要请您随时联系我。或者是方便说说您还在斟酌哪些方面吗?你还在思考,考虑的是哪……U盘就是私有云,路由就是NAS,企业级办公必备蒲公英路由器X 数字科技时代,工作的强度也是越来越大,文件表格,数据信息等都是非常重要的,定期定时就要上传上报的操作基本每天都有,我个人在通信领域工作,工作状态也就是我所说的,随时都要报表,统……IF函数这样用,才能让你的效率翻倍!90的人都还不会。。热闻 编按Hello小伙伴们,IF函数是我们在工作中使用最频繁的函数之一。当我们想知道某一个单元格中的数据是否包含指定的关键词时,我们就可以用IF函数搭配IFERROR、SUM……
劳动节的作文200字热博聚热点网 好文:三进大观园的是谁?(三进大观园是四大名著中的哪一个)热 内心的宁静比什么都重要热闻聚热点网 好文:关于汕头大学简介(汕头大学简介)热闻聚热点网 地支相破相害相穿与吉凶用法,百发百中!【算命先生提醒】热传聚 好文:甲醛是不是有味道(关于甲醛有味道么)热博聚热点网 樱桃的寓意(樱桃寓意象征着什么)热传聚热点网 介绍元宵节的作文热博聚热点网 完美世界官方网站完美手游官方网站热传聚热点网 dnf剑魂加点100版本狂人剑魂加点热博聚热点网 面筋怎么做好吃家常菜热闻聚热点网 地铁怎么买票新手如何乘坐地铁热评聚热点网
飞上天空的绵羊搜索成本大众市场与长尾理论公务员的工作月球神秘无解事件或与外星人有关大学生毕业自我鉴定中国南海发现的秘密沉船船身800多年未腐,价值3000亿美金教师请假条范文精选利用多媒体技术,在语文教学中渗透美育的论文长痘痘能用烟酰胺吗痘痘肌究竟如何选择护肤品人家的这才叫卫生间你家的只能叫厕所立冬将至,记得多给家人吃这7种肉,顺应节气,平安过冬貂蝉拜月的故事一览

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找新乡渭南铜川松江山南雅安松原荃湾淮北昭通平凉鞍山赤峰苗栗保亭池州渝北株洲陇南濮阳三沙秀山密云鸡西