3

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

 1 year ago
source link: https://www.cnblogs.com/Iflyinsky/p/16440633.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

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

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

  本文作为本人csdn blog的主站的备份。(BlogID=116)

  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交互的一些文章,感兴趣可以参考。

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<float, py::array::c_style | py::array::forcecast> &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_<MyClass, std::unique_ptr<MyClass, py::nodelete>>(m, "MyClass")
    .def(py::init<>())


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

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



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

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


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

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK