2013-10-21 15 views
2

假设我有一个包含N个元素的C字符串数组。我的目标是使用JNI将该数组传递给Java函数,并返回一个等长的新字符串数组回到C空间。目前我做如下:将C字符串数组移动到Java空间的更有效方法

  • 使用NewObjectArray以产生长度为N
  • 调用NewStringUTF/SetObjectArray N倍的Java对象阵列,至框每个单独的C字符串到Java对象阵列。
  • 调用copyStrArr(下面的源代码)。
  • 用malloc分配长度为N的(char *)数组。
  • 调用GetObjectArrayElement/GetStringUTFChars N次,以从返回的Java Object数组中解开每个单独的Java String。

仅供参考,Java代码看起来是这样的:

public static String[] copyStrArr(String []inArr) 
{ 
    String []outArr = new String[inArr.length]; 
    for(int _i = 0; _i < outArr.length; _i++) { 
     outArr[_i] = inArr[_i]; /* Normally real work would be done here */ 
    } 
    return outArr; 
} 

在“真实”的情况下,实际的工作将里面的for循环来完成,但对于标杆,我们要做的仅仅是副本数据。

对于较大的N值,这很慢。不敬虔缓慢。当从C到Java移动类似大小的整数或双精度数组并返回时,它的运行速度比String []大约快70倍。大约99.5%的时间花在装箱和拆箱数据上。在原始情况下,JNI提供了{Set,Get} ArrayRegion函数,可以将原始数组从C空间批量复制到Java空间并返回,速度更快。

有人建议我使用byte []作为中介,将数据导入Java空间,然后在Java中进行单独的String对象装箱(JVM可以优化事物)。基准测试表明,这比原来的测试表现稍差,将大部分开销转移到Java。部分原因可能是我可能无法在Java中以最优方式拆箱/装箱byte []。我做如下:

  • 分配一个充分大的字节[]与NewByteArray
  • 调用SetByteArrayRegion N次来填充字节[]
  • 调用copyBytArray(下面源)
  • 调用GetByteArrayRegion和复制整个结果返回C空间
  • 分配一个足够大的数组(char *)
  • 将N个字符串中的每一个从结果中复制到新分配的数组中。

我的Java代码如下所示:

public static byte[] copyBytArr(byte []inArr) 
{ 
    String[] tokInArr = new String(inArr, UTF8_CHARSET).split("\0"); 
    String []tokOutArr = new String[tokInArr.length]; 
    int len = 0; 
    for(int _i = 0; _i < tokOutArr.length; _i++) { 
     tokOutArr[_i] = tokInArr[_i]; /* Normally real work would be done here */ 
     len += (tokInArr[_i].length() + 1); 
    } 
    byte[] outArr = new byte[len]; 
    int _j = 0; 
    for(int _i = 0; _i < tokOutArr.length; _i++) { 
     byte[] bytes = tokOutArr[_i].getBytes(UTF8_CHARSET); 
     for(int _k = 0; _k < bytes.length; _k++) { 
      outArr[_j++] = bytes[_k]; 
     } 
     outArr[_j++] = '\0'; 
    } 
    return outArr; 
} 

在这个测试的开销约55%是在Java中度过的,其余的则花在装箱/拆箱。

有人提出我的一些开销与我在C中使用UTF-8数据有关,因为Java使用UTF-16。这是不可避免的。

有没有人有任何想法,我可能会更有效地去做这件事?

+4

移动大量数据总是很慢,并且在JNI边界上移动任何东西都很慢。你真的必须这样做吗? – EJP

+1

可以用char或其他东西代替String对象吗?因为原始类型处理速度比JVM中的对象更快 – user4127

+1

@Radiodef我不希望我的名字与任何'JNI中没有任何内容是有效的'声明相关联。无效的是将数据移动到JNI边界,这是因为它*是*数据移动。如果您使用100%Java或100%C,则不必移动任何东西。 JNI中的大部分内容都非常高效,在某些情况下效率也很高,因为没有错误检查。 – EJP

回答

1

我认为你的问题是分配很多字符串对象。为了获得真正的性能,您只需要交换大字节[]并使用Wrapper Classes“指向”字符串数组进行字符串处理。只要你从C字符[]来回创建字符串对象,你就不会得到真正的吞吐量。

FST正在做一些与“StructString”类相似的操作来处理byte []数据,而不需要创建“真实”对象。

要进一步加速数据交换,您可能需要使用内存映射文件创建共享内存,并通过不安全或ByteBuffers访问此内存。

+0

我同意大部分的开销与Strings的装箱和拆箱有关。我们可以将大分隔字节[]以及可以作为索引向量的int []传递到字节[]中。所以delimitedByteArr [indexArr [i]]指向第i个字符串的第一个字节。不幸的是,这可能不是合理的妥协(当我们的要求可能会限制我们最终以String []结束时)。如果我们要获得更好的吞吐量,那么需要审查这个需求。 – TheGeneral

+0

它不是“装箱”它的String对象和char []数组对象(包含在String类中)的分配。 你的“N”有多大? 像每秒500 MB的传输不成问题(没有任何处理这些数据:-))) –

+0

@ R-Moeller N的范围可以从400万到1亿。每个元素的大小可以从1到255个字节不等,但会有一些例子通过这个接口传递更多的数据。在低端我们正在使用几兆字节,高端数十千兆字节。目前我们发现int []情况和String []情况之间的速度差异是70倍,这很令人担忧。 Object-ify和unobject所花费的时间 - 如果1000万char []总计78MB数据的时间大约是5s,这非常缓慢。总计38MB数据的1000万英镑需要约67ms。 – TheGeneral

相关问题