简体   繁体   English

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

[英]A more efficient way of moving an array of C strings into Java space

Let's say I have an array of C strings with N elements. 假设我有一个包含N个元素的C字符串数组。 My goal is to pass that array into a Java function using the JNI and return a new string array of equal length back to C space. 我的目标是使用JNI将该数组传递给Java函数,并将相等长度的新字符串数组返回给C空间。 At present I am doing the following: 目前,我正在执行以下操作:

  • Using NewObjectArray to generate a Java Object array of length N. 使用NewObjectArray生成长度为N的Java对象数组。
  • Calling NewStringUTF/SetObjectArray N times, to box each individual C string into the Java Object array. N次调用NewStringUTF / SetObjectArray,以将每个单独的C字符串装箱到Java Object数组中。
  • Invoking copyStrArr (source below). 调用copyStrArr(以下来源)。
  • Allocating a length N array of (char *) with malloc. 用malloc分配长度为(char *)的N个数组。
  • Calling GetObjectArrayElement/GetStringUTFChars N times, to unbox each individual Java String from the returned Java Object array. 调用N次GetObjectArrayElement / GetStringUTFChars,以从返回的Java Object数组中拆箱每个单独的Java String。

For reference, the Java code looks like this: 作为参考,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;
}

In the "real" case actual work would be done inside the for loop, but for benchmarking we're just making a copy of the data. 在“真实”情况下,实际工作将在for循环内完成,但是对于基准测试,我们只是复制数据。

For large values of N this is slow. 对于较大的N,这很慢。 Ungodly slow. 太慢了。 When moving a similarly sized array of ints or doubles from C to Java and back it runs ~70x faster than the String[] case. 当将大小相似的int或double数组从C移到Java并返回Java时,它的运行速度比String []情况快70倍。 about 99.5% of the time is spent boxing and unboxing the data. 将数据装箱和拆箱的时间大约占99.5%。 In the primitive case the JNI offers {Set,Get}ArrayRegion functions to batch copy primitive arrays from C space to Java space and back, which is much faster. 在原始情况下,JNI提供了{Set,Get} ArrayRegion函数来将原始数组从C空间批量复制到Java空间,然后再复制回Java空间,这要快得多。

It has been suggested I use a byte[] as an intermediary to get the data into Java space, and then do the individual String Object boxing in Java (where the JVM can optimize things). 有人建议我使用byte []作为中介,将数据放入Java空间,然后在Java中进行单独的String Object装箱(JVM可以在其中进行优化)。 Benchmarks have shown that this performs slightly worse than the original test, moving much of the overhead into Java. 基准测试表明,此方法比原始测试的性能稍差,将大量开销转移到了Java中。 Part of this might be that I may not be optimally unboxing/boxing the byte[] in Java. 部分原因可能是我可能没有最佳地对Java中的byte []取消装箱/装箱。 I am doing the following: 我正在执行以下操作:

  • Allocating a sufficiently large byte[] with NewByteArray 用NewByteArray分配足够大的byte []
  • Calling SetByteArrayRegion N times to populate the byte[] 调用SetByteArrayRegion N次以填充字节[]
  • Invoking copyBytArray (source below) 调用copyBytArray(以下来源)
  • Calling GetByteArrayRegion and copying the whole result back into C space 调用GetByteArrayRegion并将整个结果复制回C空间
  • Allocating a sufficiently large array of (char *) 分配足够大的(char *)数组
  • Copying each of the N Strings out of the result into the newly allocated array. 将N个字符串的每一个从结果中复制到新分配的数组中。

My Java code looks like this: 我的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;
}

In this test about 55% of the overhead was spent in Java, and the rest was spent boxing/unboxing. 在此测试中,大约55%的开销花在了Java上,其余的花在了装箱/拆箱上。

It has been suggested that some of my overhead is related to the fact that I an using UTF-8 data in C, since Java uses UTF-16. 有人建议我的一些开销与我在C语言中使用UTF-8数据有关,因为Java使用UTF-16。 This is unavoidable. 这是不可避免的。

Does anyone have any ideas about how I might go about this more efficiently? 有人对我如何更有效地解决这个问题有任何想法吗?

I think your problem is allocation of many string objects. 我认为您的问题是分配了许多字符串对象。 In order to get real performance you need to just interchange large byte[] and use Wrapper Classes "pointing" to the byte array for String processing. 为了获得真正的性能,您只需要交换大的byte []并使用Wrapper类“指向”字节数组进行String处理。 As long you are creating String Objects from C chars[] back and forth, you won't get real throughput. 只要您通过C chars []来回创建字符串对象,就不会获得真正的吞吐量。

FST is doing something similar with the "StructString" class to operate on byte[] data without the need to create "real" Objects. FST正在与“ StructString”类进行类似的操作,以对byte []数据进行操作,而无需创建“真实”对象。

To speed up dataexchange further you might want to create shared memory using memory mapped files and access this via Unsafe or ByteBuffers. 为了进一步加快数据交换的速度,您可能需要使用内存映射文件创建共享内存,然后通过Unsafe或ByteBuffers进行访问。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM