2011-09-28 41 views
1

我需要優化csv文件(字符串)的實際加載/解析。我知道的最好的方法是就地加載算法,並且我成功地使用了JNI和一個C++ dll,它直接從一個由解析的csv數據構成的文件中加載數據。java字符串優化 - 就地加載算法

如果它停在那裏,它會好起來的,但是使用該方案只會使速度提高15%(不再解析數據)。其中一個原因並不像我第一次想到的那樣快,因爲java客戶端使用jstring,所以我需要再次將實際數據從char *轉換爲jstring。

最好的辦法是忽略該轉換步驟並將數據直接加載到jstring對象中(不再進行轉換)。因此,不是基於現場加載的數據來複制數據,而是將jstring直接指向內存塊(請注意,數據將由jchars而不是字符組成)。真正的壞處是,我們需要確保垃圾收集器不會收集數據(通過保持對它的引用可能?),但它應該是可行的。

我想我有兩個選擇這樣做:

1加載Java中的數據(不超過JNI),並使用都指向加載的數據創建串字符..但我需要找到一種方法來防止在創建字符串時複製數據。

2-繼續使用jni「手動」創建並設置jstring變量,並確保垃圾收集器選項設置正確,以防止它做任何事情。例如:

jstring str; 
str.data = loadedinplacedata; // assign data pointer 
return str; 

不知道這是可能的,但我不介意只是直接保存的jstring到文件並重新加載它這樣:

jstring * str = (jstring *)&loadedinplacedata[someoffset]; 
return * str; 

我知道,這是不是通常的Java事情,但我非常確定Java可擴展性足以做到這一點。並不是說我真的有這個選擇......項目已經3年了,需要運作。 = S

這在JNI代碼(C++):

const jchar * data = GetData(id, row, col); // get pointer of the string ends w/ \0 
unsigned int len = wcslen((wchar_t*)data); 
// The best would be to prevent this function to duplicate the data. 
jstring str = env->NewString(data, len); 
return str; 

注:上面的代碼使它更快(而不是15)20%通過使用Unicode數據代替UTF8(NewString代替NewStringUTF) 。這表明,如果我可以刪除該步驟或優化它,我會獲得相當不錯的性能提升。

+1

這是從哪裏來的?如果拷貝花費的時間比磁盤IO長,假設沒有什麼真正的愚蠢行爲,我會感到驚訝。 –

+0

- 1)您是否需要將整個文件同時存儲在內存中? - 2)爲什麼要使用JNI? – claymore1977

+0

它通常是在開發環境中從.jar文件和文件直接加載的。所以你可以假設幾乎沒有磁盤IO,因爲它應該已經被加載了。 我不需要它在內存中的所有文件,但文件應該已經存在,因爲它是jar文件的一部分。 JNI讓我使用指針...但是由於我用C++有更多的xp,我可能會誤解一些Java的特性。有什麼辦法可以防止數據的複製(如根據內存中的位置重新使用引用?) – MasterPlanMan

回答

0

呃......好像我想做的事情不是被Java「支持」,除非我破解它..我相信有可能通過使用GetStringCritical來獲得實際的char數組地址,然後找到超出字符的數量等等,但這遠遠超出了「安全」編程。

我發現最好的解決方法是在java中創建一個哈希表,並在創建我的數據文件時使用唯一的標識符處理(類似於.intern())。如果字符串不在散列表中,它將通過dll查詢並將其保存在散列表中。

數據文件: numRow行,數numCols, 對於每個小區,添加一個整數值(在我的情況下的偏移量在存儲器指向字符串) 針對每個小區,添加字符串\ 0

通過結束使用偏移量值,我可以稍微減少字符串創建和字符串查詢的數量。我嘗試使用globalref來保持DLL中的字符串,但它使它慢了4倍。

0

我從來沒有使用過JNI,但是......讓它返回一個實現CharSequence的自定義類,也許還有一些其他接口,比如CharSequence>而不是字符串?看起來你不太可能有這樣的數據損壞問題。

+0

這可能是值得嘗試的。例如,如果我可以創建一個類,使我可以重用加載的數據(來自內存流或字節數組之類的東西),而不必訴諸於複製數據。但是這迫使我重做大部分的String類函數。將該類與哈希表兼容也很容易嗎? – MasterPlanMan

+0

@Adam - 當然 - 只是實現hashcode()和equals()。它們的尺寸很小,但仍需要小心。使用JDK字符串來源作爲參考。你可能也會想toString()。 –

+0

是否可以定義字符串類的子代?因爲有很多需要字符串對象的地方..所以創建這個類只會推遲數據的重複。= S否則,我將不得不改變代碼,在它請求一個字符串並更改它,因此它請求一個charsequence 。 – MasterPlanMan

0

我認爲首先您必須瞭解爲什麼C++版本運行速度提高15%,以及爲什麼性能改進不能直接轉換爲Java。爲什麼不能在Java中將代碼編寫速度提高15%?

讓我們看看你的問題。你已經通過使用C++ dll消除了解析。 (爲什麼不能在Java中完成?)。然後,我的理解是:

  1. 你提出直接
  2. 你想防止垃圾回收器觸摸這些修改jstrings(通過保持對其引用)來操縱jstrings的內容,因此可能會修改JVM的行爲,並在垃圾收集器最終進行垃圾收集時搞砸垃圾收集器。

在你允許它們被垃圾收集之前,你會「修復」這些引用嗎?

如果你建議做自己的內存管理,爲什麼你使用java呢?爲什麼不用純粹的C++來做呢?

假設您希望繼續使用Java,當您創建一個String時,它本身就是一個新的Object,但它指向的數據不一定。你可以通過調用String.intern()來測試它。使用下面的代碼:

public static void main(String[] args) { 
    String s3 = "foofoo"; 

    String s1 = call("foo"); 
    String s2 = call("foo"); 

    System.out.println("s1 == s2=" + (s1 == s2)); 
    System.out.println("s1.intern() == s2.intern()=" + (s1.intern() == s2.intern())); 
    System.out.println("s1.intern() == s3.intern()=" + (s1.intern() == s3.intern())); 

    System.out.println("s1.substring(3) == s2.substring(3)=" + (s1.substring(3) == s2.substring(3))); 
    System.out.println("s1.substring(3).intern() == s2.substring(3).intern()=" + (s1.substring(3).intern() == s2.substring(3).intern())); 
} 

public static String call(String s) { 
    return s + "foo";   
} 

這將產生:

s1 == s2=false 
s1.intern() == s2.intern()=true 
s1.intern() == s3.intern()=true 
s1.substring(3) == s2.substring(3)=false 
s1.substring(3).intern() == s2.substring(3).intern()=true 

所以你可以看到,儘管String對象是不同的數據,實際的字節都沒有。因此,您的修改可能實際上並不相關,JVM可能已經在爲您做這些工作。值得一提的是,如果你開始修改jstrings的內部結構,這很可能會導致這種情況發生。

我的建議是找出你可以在算法方面做些什麼。使用純Java進行開發總是比較快速,因爲它可以結合使用Java。使用純Java可以找到更好的解決方案。

+0

該項目是在Java(這是一個3歲),所以我不能真正改變爲C++,除非它是通過jni的某些特定情況。我寧願保持java而不是使用C++。 我的目標是不要讓代碼快15% - 我需要它更快。所以就地加載是一個很好的方法來嘗試做到這一點 - 雖然我接受新的建議。 =) 現在,我不想做我自己的內存管理,但我認爲這將防止任何問題..但從我可以看到,我們可以使用外部緩衝區設置字符串數據?那麼可以防止任何重複的數據? – MasterPlanMan

+0

此外,還有一些方法可以通過使用不同的「格式」和包含字符串位置的索引來減少通過java進行解析的數量......但最好不會解析。在C++中這很容易,但是在java中很難做到(至少對我來說)..因此我的問題給你們。 – MasterPlanMan

+0

「,但它指向的數據不一定是」 - 這正是我試圖做的事實。如何指定數據來自char [],byte [],jni等? (不是已經定義好的字符串) 如果可以這樣做的話,我只需要用Java加載數據(沒有更多的jni!),並且直接指定字符串數據而不重複。 – MasterPlanMan