栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

jvm jni 及 pvm pybind11 大批量数据传输及优化

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

jvm jni 及 pvm pybind11 大批量数据传输及优化

PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

环境说明
  1. android 手机
  2. linux python环境
前言

  近几个月来,对我来说,发生了许许多多的事情,导致有很多idea,但是都未形成好的文章。最近,趁着这个机会,写一篇。

  由于业务的安排,我们需要在c/c++层与java和python层进行数据交换,数据量有大有小,但是由于我们业务上对这个数据交换的延时有一定的要求,因此有些问题需要我们解决。在我们的实验过程中,我们发现了在常规情况下,在jvm中用新创建ByteArray/FloatArray进行大数据量(6Mb byte/2Mb floats)的传输,时间在5ms/7ms,在pvm中用新创建bytearray大数据量(8Mb byte)的传输,时间在1ms左右。从实验情况来看,我们需要优化jvm中进行大数据量传输的方法。

  我以前写过关于java,python和c/cpp交互的一些文章,感兴趣可以参考。

  • 《C++ 调用 Python 总结(一)》 https://blog.csdn.net/u011728480/article/details/103903612
  • 《java 手动生成jni头文件(JNI静态注册)》 https://blog.csdn.net/u011728480/article/details/87260113
  • 《Android JNI静态和动态注册 、Java Reflect(C或C++层反射和JAVA层反射)、Java 可变参数(JNI实现)》 https://blog.csdn.net/u011728480/article/details/78963494




jvm jni篇

  jni常规大量数据交换方法网上有许多,基本都是如下所示:

  在java往c/cpp返回时,一般都是获取数据的底层地址,然后针对地址操作即可。

jbyteArray array;//or jfloatArray array; passed by jni-func
void * _you_wanted_ptr = env->GetPrimitiveArrayCritical(array, nullptr);

// TODO

env->ReleasePrimitiveArrayCritical(array, _you_wanted_ptr, JNI_ABORT);

  在c/cpp往java传输大量数据时,有两种方式,一种是直接new一个数组,然后返回的方式,一种就是获取java层的数组地址,然后直接修改相关的数据即可。其基本如下所示:

// slow way
int len = xxx;
void * data_ptr = xxx;
jXXXArray array = env->NewXXXArray(len);
env->SetXXXArrayRegion(array, 0, len, (const jXXX *) data_ptr);
return array;

// fast way
jbyteArray array;//or jfloatArray array; passed by jni-func
int len = xxx;
void * data_ptr = xxx;
env->SetXXXArrayRegion(array, 0, len, (const jXXX *) data_ptr);

  这里在使用fast way模式后,在jvm中用进行大数据量(6Mb byte/2Mb floats)的传输,时间在0.88ms/1ms,注意,有使用限制。这里一定要注意多线程安全的问题。





pvm pybind11篇

  在pybind11中,大规模数据传输一般有两种数据结构,一种是py::bytes,一种就是我们常见的numpy数组,特别是在图像处理中,numpy数组是最常见的一种格式。下面,根据这两种方式,分别介绍。



py::bytes 类型传输

  python 层传给c/cpp。

const py::bytes &value;//passed by pybind11-func
Py_ssize_t size = PyBytes_GET_SIZE(value.ptr());
char * ptr = PyBytes_AsString(value.ptr());

//TODO 

  c/cpp 层传给python。

char * buf = xxx;
int len = xxx;
return py::bytes(buf, len);//In pybind11, return to pvm

  注意,在py::bytes中,也有直接修改地址的方式,这里就不提供了(python buffer protocol),有心人自己去研究吧。



numpy数据传输

  这个也有像py::bytes那样创建数组,然后返回的方式,这里就不提供了。这里主要还是演示一下怎么快速在c/cpp中获取numpy数据。其实这里的数据传输也就是直接获取numpy数组地址,基本大差不差。

  c/cpp到python

// python buffer protocol
py::array_t &buffer;//passed by pybind11-func
auto buf_info = buffer.unchecked<1>();

char * ptr = (char *)buf_info.data(0)

// set value to ptr(numpy)

// get value from ptr(numpy)

  注意,这里使用到一个叫做python buffer protocol的东西,有兴趣大家可以看看,我在这个上并没有深究。



pybind11中内存管理问题

  在pybind11中,要小心管理内存,特别是注意以下两种调用的区别。
根据https://pybind11.readthedocs.io/en/stable/advanced/classes.html#non-public-destructors的说明,我们一般会有两种情况需要选择使用。

// 单例
class MyClass{
    private:
    ~MyClass(){}
};

// 禁止unique_ptr 调用 析构函数, 所有资源释放需要在cpp侧进行完成。
py::class_>(m, "MyClass")
    .def(py::init<>())


// 一般class
class MyClass{
    public:
    ~MyClass(){}
};

// unique_ptr 析构时自动调用析构函数,所有资源释放由unique_ptr完成。
py::class_>(m, "MyClass")
    .def(py::init<>())





后记

  总的来说,在jvm和pvm中,通过操作固定数组的底层指针,我们可以快速的获取数据和传输数据。但是存在一些现象,例如需要注意一些原子操作和pvm/jvm中数组的生命周期的问题,我这里建议,如果是大规模数据传输,建议直接全局数组,这样保证生命周期问题。

参考文献

[1]https://pybind11.readthedocs.io/en/stable/advanced/classes.html#non-public-destructors




打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1000929.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号