将C字符串数组移动到Java空间的更有效方法

时间:2013-10-21 23:54:10

标签: java c arrays string java-native-interface

假设我有一个包含N个元素的C字符串数组。我的目标是使用JNI将该数组传递给Java函数,并将一个等长的新字符串数组返回给C空间。目前我正在做以下事情:

  • 使用NewObjectArray生成长度为N的Java对象数组。
  • 调用NewStringUTF / SetObjectArray N次,将每个单独的C字符串打包到Java Object数组中。
  • 调用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 Object装箱(JVM可以在其中进行优化)。基准测试表明,这比原始测试略差,将大部分开销转移到Java中。部分原因可能是我可能无法在Java中最佳地拆箱/装箱byte []。我正在做以下事情:

  • 使用NewByteArray
  • 分配足够大的byte []
  • 调用SetByteArrayRegion N次以填充byte []
  • 调用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。这是不可避免的。

有没有人对如何更有效地解决这个问题有任何想法?

1 个答案:

答案 0 :(得分:1)

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

FST正在使用类似于“StructString”的类来操作byte []数据,而无需创建“真实”对象。

为了进一步加速dataexchange,您可能希望使用内存映射文件创建共享内存,并通过Unsafe或ByteBuffers访问它。