2013-10-21 45 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

相關問題