rust能造多高为什么别人第一天就全高精

版权声明:本文为博主原创文章遵循

版权协议,转载请附上原文出处链接和本声明

  两行,表示两个非负整数a、b且有a > b。
  一行表示a与b的差

1.被减数某一位小于減数对应位,需要借位
2.如果被借位,分类讨论如果此为为0,需要向前借位一直到不为0为止。
3.输入数据较大采用string类型输出,字符型與整型数据变换不要混乱
4.输出结果时候,要将数剧前面的0去掉就是A:00020,B:20就是我们计算出来的结果是A,但是我们要输出的样式是B

部门算法团队开始成长起来开始有越来越多的尝试以及成果,但是现在工程方面严重的限制了(主要是做预测服务)他们的研究成果转化为实际输出的能力去年下半姩,我们就发现TF官方的Java binding 存在严重的内存泄露问题而TF Java binding 因为封装包括训练和预测所需要的API,比较复杂,我们也难以更改同时,使用TF serving就需要提供标准的RPC调用来完成交互,而所有的数据处理等工作都是在Java端这也对运维模式产生一定的压力,毕竟要维护serving集群研发工程师要对接serving財能完成一个端到端的预测。

其实最简单的办法还是提供java binding,但是这个binding我们只提供Load model 以及tensor in tensor out 的predict,这样对我们的封装要求就可以小很多而对于C/C++我个人並不是很熟悉,正好我最近开始学习rust能造多高(主要还是看到rust能造多高更符合我的编程习惯)所以打算用rust能造多高对TF进行二次封装,然后暴露出一个简单的C ABI供Java做绑定 这里就会涉及到rust能造多高 FFI的使用,目前网络上资源也比较少更多的是Example性质的,大家的文章大同小异所以我這里就简单写下我这两天折腾的心得。代码还比较烂欢迎大家指正,但勿喷

语言之间能够交互的核心原因在于最终他们都会被编译为基于特定系统(如Linux)二进制文件,这种底层的共通性就为他们带来了直接交互的可能性现阶段,如果要跨多语言最好的方式是都遵循C ABI標准。

这里业务逻辑比较简单根据流程,我们只要提供五个核心函数即可分别是:

因为我们是希望用rust能造多高对TF做二次封装,这里我們直接使用crate rust能造多高-tf, 这样可以避免再次对TF原生的libtensorlfow做封装毕竟工作量是不小的。

前面的定义涉及到了 CTensor, OutputTensor, Predictor_t 等几个结构体他们本质上对应的都昰高级语言里的对象。因为rust能造多高 支持和C一样的结构体布局所以我们可以在两个语言之间直接传递结构体。下面是这几个结构体在C侧嘚定义:

Predictor_t, OutputTensor 我们看只是定义了一个空结构体因为这两个东西对应的是rust能造多高返回的对象。在FFI里我们可以使用一个空的struct 对象来代替一个實际的rust能造多高对象,然后通过指针来进行应用什么意思的呢?比如下面代码:

这里的load其实对应的是rust能造多高里的函数该函数返回了┅个包装了TFSession的对象。我们通过Predictor_t来表示它predictor虽然是指向Predictor_t的指针,但本质上是指向包装TFSession对象的指针虽然在C里我们不能直接调用Predictor_t的方法,但是峩们可以提供一些辅助方法将Predictor_t传递回rust能造多高然后在rust能造多高调用里调用他的方法最后返回结果。比如

下面是是predict方法的签名以及在rust能慥多高里的实际上实现:

所以透明指针只是对一个对象的实际引用,方便我们在调用语言侧传递同样的,我们也可以看到两种语言直接的交互,都可以通过指针来完成

如何在C/rust能造多高之间传递指针

首先,rust能造多高 的函数要返回一个指针可以像下面那么做:

接着我们茬C语言里申明函数头,就能使用rust能造多高里的这个函数实现了:

那如何传递一个指针给rust能造多高函数呢 我们看下面的代码:

获取一个C传遞回来的指针后,需要对他进行处理之后才能在rust能造多高里面使用:

如果我想传递数组怎么办

数组使用太频繁了,那么C/rust能造多高 应该如哬传递数组呢本质上我们是没办法直接传递数组的,除了普通的值类型一切都是以指针进行交互的。在C里面数组和指针具有很大的楿关性,我们可以利用指针来模拟数组我们以字符串为例子,因为对于字符串不同语言的表示形态也是不一样的,但是都可以用char(u8)来表礻所以我们可以把字符串看成u8的数组。一个数组其实由两部分组成:

  1. 一片连续的元素(内存)

我们只要知道这片连续元素的起始地址鉯及元素的个数,就能描述这个数组所以通过下面的struct 就能描述数组:

这样,data是指向char的指针同时我们也可以认为是数组第一个元素的指針,然后我们提供了该指针指向数组的长度

接着我们在rust能造多高定义各一个相同的结构体,并且提供一个函数供C调动。

现在提供一个C的函数签名:

现在就可以在C侧调用了:

然后大家就看到了rust能造多高侧打印了如下内容:

知道了字符串是怎么处理的,那么我们想传递一个张量该怎们办呢张量本质上由两部分组成:

  1. 一个存储实际数据的数组(一维)
  2. 描述形状的数组 (一维)

所以其实是两个数组,前面我们知噵描述一个数组只要一个指针和一个长度就可以了,所以我们描述一个张量可以这么做:

对应rust能造多高的结构为:

这样就实现了在C/rust能造哆高之间实现了张量的交换

前面我们看到,数组里面还都是一些基本类型那如果数组里面是个对象怎么办?比如我希望提供一个张量数组,其实没有什么差别申明大概是这样的:

只不过除了基础类型以外,一切都是要以指针传递所以这里的数据data是一个指针,这个指针指向CTensor的指针使用起来大概是这样的:

由此可见,你是可以传递任意复杂的东西的不过代价也比较高。

所有权在rust能造多高/C之间的转迻

我们知道rust能造多高是一门内存安全的问题响应的有所有权和申明周期的问题。所以在做跨语言交互的过程会遇到一些相关的问题

首先,一个对象如果传递给了调用者那么所有权会转移到调用者,这个是由

其次借出的对象一旦重新返回rust能造多高,那么所有权就转移回rust能造多高了,这个也是由

所以下面代码大家发现什么问题了么?

重新获取了所有权那么这个时候调用者(也就是前面的 Predictor_t *pre)指向的pre 就成了野指针了。接着在第二次使用的时候就会出现错误。同样的tarray也会自动被释放无法使用两次。

其实我们希望pre能够完全由调用者来决定是否釋放有解决办法么?其实本质在于from_raw会获取所有权所以我们只要不使用他就行,使用如下方式:

这里面我们只是简单的解应用pre然后获取哋址避免去获取所有权。 不过所有权虽然带来麻烦但是同时也能简化内存释放的问题。

所有权导致的另外一个空指针问题

这段代码接受了tensor,返回ctensor 在前面,我们获取了tensor的使用权接着我们够着CTensor的时候使用了tensor的引用,然后我们返回了ctensor. 但是返回之后tensor就被释放掉了,导致ctensor对tensor的引用成了野指针

很多场景下,我们确实需要一个包装对象那怎么解决这个问题呢?我一开始想到的是不释放tensor就可以了:

但这样会导致內存泄露因为我们没有其他地方可以调用tensor并且进行释放。而且forget tensor,其实是将tensor的所有权转给了ManuallyDrop 对象

其实比较好的办法是不获取tensor的所有权,

缺點也就来了调用方需要去释放tensor。

还有第三个办法就是提供一个对象该对象有一个to_ctensor方法,我们在to_tensor里调用这个对象的to_ctensor方法:

这样在C端可以這么使用了:

其原理也很简单把tensor的所有权绑定到了OutputTensor身上。

跨语言交互本身是比较难的尤其是指针问题,这也是为什么C/C++更容易写出不安铨的代码我们应该尽量使用rust能造多高 Safe部分来完成我们的逻辑。

参考资料

 

随机推荐