erlang、c 跨语言调用设计

我们的代码仓库里面存在很多C语言的库,基于运行效率或工作量的考虑,我们不想
使用erlang重写相关库,我们想在erlang中像使用erlang库一样方便地使用C库,
vcall(erlang版)正是在这种背景下出现的。vcall(erlang版本)后面简称
vcall。


使用vcall时,我们只需要调用register接口批量
或单个注册我们要使用的函数,接下来我们就可以像使用erlang库提供的函数一样
使用被注册函数。vcall支持动态替换和卸载c库。

      


## 实现原理


vcall分为两部分:erlang部分(erl_vcall.beam,npc.beam);c部分(erl_vcall.dll
或 erl_vcall.so)。两部分之间使用连接符合nif相关约定。erlang部分对外(用户)
提供函数注册、动态替换c库、清理c库接口。c部分对erlang提供函数注册、函数函数
调用、动态卸载c库、动态加载c库、更新函数索引等接口。




函数注册(正常)过程:用户把函数相关信息传给erlang;erlang调用c注册接口;通过
动态库名、函数名、函数形参类型及个数,获取函数指针,并保存在全局变量中;erlang
将函数函数注册信息保存到ets表中(防止函数重复注册),并动态生成模块名、函数名、
形参个数和c函数一致的erlang代码;更新c中的全局变量的函数索引。


函数调用过程 :调用注册函数过程中生成的erlang函数(参考下面vcall使用示例);
erlang调用C对应调用接口;c解析参数并做参数的类型转换,以符合ffi的调用约定;
把参数、注册时保存的函数指针传递给ffi通用接口,然后把结果返回给erlang。
    
动态替换c库过程:调用相关接口,卸载原有c库并删除文件;拷贝新库到原有位置,重新
注册旧库中被注册的函数(注册信息从ets表中获取)。


## vcall使用示例


### 准备工作


erlang运行环境;vcall库编译生成对应文件,并放到相应目录下;被测试c库放到
erl_vcall.so(windows下erl_vcall.dll)同一目录下。


### 被测试动态库


被测试动态库名称为test(windows下test.dll,linux下test.so),源码如下


int add(int* a, int* b) {
return (*a + *b);
}


int* createInt(int a) {
int* p = (int*) malloc(sizeof(int));
*p = a;
return p;


}


void destroyInt(int* p) {
free(p);
}


int add2(int a, int b){
return a + b;
}


### 函数注册调用过程


调用npc:start_link([]).启动lpc模块(只需要执行一次)。


调用npc:register("test","int","add2","int,int",syn)注册函数。


接下来就可以使用 test:add(2,3)返回计算结果5。


其他函数的注册调用方法和上面的示例步骤一致。


## 性能测试


### 测试函数


int* createInt(int a);


int add2(int,int);


int add3(double,double);


int multiDouble(double,double,double,double,double,
double,double,double,double,double,double,double,
double,double,double,double,double,double,double,double);


int multiInt(int,int,int,int,int,int,int,int,int,int,
int,int,int,int,int,int,int,int,int,int);


### 测试方法 


通过erlang库函数调用以上函数1000000次,求出函数调总耗时和平均耗时;通过调用空函数1000000次,求出nif层消耗的总时间和平均时间。


### 测试结果


#### 32位


<table>
    <tr>
        <td>函数</td>       
        <td>总耗时(微秒)</td>
        <td>平均耗时(纳秒)</td>
    </tr>
    <tr>
        <td>createInt</td>       
        <td>444226</td>
        <td>444</td>
   
    </tr>
    <tr>
        <td>add2</td>        
        <td>419352</td>
        <td>419</td>
   
    </tr>
    <tr>
        <td>add3</td>         
        <td>417791</td>
        <td>417</td>    
    </tr>
    <tr>
        <td>multiInt</td>        
        <td>1041342</td>
        <td>1041</td>
    </tr>
    <tr>
        <td>multiDouble</td>       
        <td>1356512</td>
        <td>1356</td>
      
    </tr>  
    <tr>              
        <td>nif层</td>       
        <td>56113</td>
        <td>56</td>
    </tr>          
</table>   


#### 64位
<table>   
    <tr>
        <td>函数</td>       
        <td>总耗时(微秒)</td>
        <td>平均耗时(纳秒)</td>
    </tr>
    <tr>
        <td>createInt</td>       
        <td>341248</td>
        <td>341</td>
   
    </tr>
    <tr>
        <td>add2</td>        
        <td>336733</td>
        <td>336</td>
   
    </tr>
    <tr>
        <td>add3</td>         
        <td>302804</td>
        <td>302</td>    
    </tr>
    <tr>
        <td>multiInt</td>        
        <td>1035253</td>
        <td>1035</td>
    </tr>
    <tr>
        <td>multiDouble</td>       
        <td>1069095</td>
        <td>1069</td>
      
    </tr>  
    <tr>              
        <td>nif层</td>       
        <td>72439</td>
        <td>72</td>       
    </tr>          
</table>   




#### 结论


1.函数调用64位系统稍快于32位,nif层64位耗时比32位长


2.函数调用的时长与参数类型关系不大,调用时长随参数个数的增加而增加


## 不足


目前vcall仅支持c基本数据类型,不支持复杂数据类型,不支持可变参数函数,
不支持函数重载。

 

ps:本文仅提供思路,源码并未开源恕不能提供。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值