[英]Reading file takes too long
我的應用程序首先從SD卡中解析了一個約100MB的文件,然后花了幾分鍾時間。 為了弄清楚這一點,在我的PC上,解析同一文件需要幾秒鍾。
我首先使用Matcher和Pattern天真地實現了解析器,但是DDMS告訴我90%的時間都花在了計算正則表達式上。 而且花了半個多小時來解析文件。 該模式非常簡單,一行包括:
ID (a number) <TAB> LANG (a 3-to-5 character string) <TAB> DATA (the rest)
我決定嘗試使用String.split 。 它沒有顯示出明顯的改進,可能是因為此函數本身可能使用了正則表達式。 到那時,我決定完全重寫解析器,最終得到如下結果:
protected Collection<Sentence> doInBackground( Void... params ) {
BufferedReader reader = new BufferedReader( new FileReader( sentenceFile ) );
String currentLine = null;
while ( (currentLine = reader.readLine()) != null ) {
treatLine( currentLine, allSentences );
}
reader.close();
return allSentences;
}
private void treatLine( String line, Collection<Sentence> allSentences ) {
char[] str = line.toCharArray();
// ...
// treat the array of chars into an id, a language and some data
allSentences.add( new Sentence( id, lang, data ) );
}
我注意到了巨大的推動力。 用了幾分鍾而不是半個小時。 但是我對此並不滿意,因此我分析並意識到瓶頸是BufferedReader.readLine 。 我想知道:這可能是受IO約束的,但也可能是花了很多時間來填充我真正不需要的中間緩沖區。 因此,我直接使用FileReader重寫了整個過程:
protected Collection<Sentence> doInBackground( Void... params ) {
FileReader reader = new FileReader( sentenceFile );
int currentChar;
while ( (currentChar = reader.read()) != -1 ) {
// parse an id
// ...
// parse a language
while ( (currentChar = reader.read()) != -1 ) {
// do some parsing stuff
}
// parse the sentence data
while ( (currentChar = reader.read()) != -1 ) {
// parse parse parse
}
allSentences.add( new Sentence( id, lang, data ) );
}
reader.close();
}
當我意識到性能非常糟糕時,我感到非常驚訝。 顯然,大部分時間都花在FileReader.read上 。 我想只讀一個字符會花很多錢。
現在我有點靈感了。 有小費嗎?
這可能會提高性能的另一種選擇是使用InputStreamReader
周圍FileInputStream
。 您必須自己進行緩沖,但這絕對可以提高性能。 有關更多信息,請參見本教程 -但是不要盲目地跟隨它。 例如,當您使用char數組時,可以將char數組用作緩沖區(並在到達treatLine()
時將其發送到treatLine()
)。
另一個建議是直接使用Thread
。 關於AsyncTask
文檔說(我的語氣):
AsyncTask被設計為圍繞Thread和Handler的幫助器類,並且不構成通用的線程框架。 理想情況下,應將AsyncTasks用於較短的操作(最多幾秒鍾)。如果需要使線程長時間運行,則強烈建議您使用java.util.concurrent pacakge提供的各種API,例如執行程序,ThreadPoolExecutor和FutureTask。
另外,獲得更快的SD卡肯定會有所幫助-這可能是其比台式機慢得多的主要原因。 普通的HD可以讀取60 MB / s,而慢速的SD卡可以讀取2 MB / s。
我想您需要保留BufferedReader,但可能不使用readline。 FileReader從最慢的SD卡讀取內容。 BufferredReader從內存中讀取,效果更好。 第二種方法增加了您訪問Filereader.read()的時間,我想這行不通。
如果readline()很耗時,請嘗試以下操作:
reader.read(char[] cbuf, int off, int len)
嘗試一次獲取大量數據。
刪除BufferedReader會使情況變得更糟。 當然。 您確實需要“填滿中間緩沖區”。 使用FileReader
目錄每個字符時,它可以節省8191個系統調用中的8191個。 緩沖的I / O總是更快。 我不知道你為什么會想到別的。
如@EJP所述,您應該使用BufferedReader。 但從根本上講,您正在移動設備上運行,而不是PC。 閃存的讀取速度遠遠不能與PC相比,其計算能力僅是運行於3.5 GHz的4核8線程i7的一小部分,我們甚至都沒有考慮過以全速運行閃存和CPU的情況。會影響設備的電池壽命。
因此,您應該問自己的真正問題是,為什么您的應用程序需要解析100 MB的數據? 而且,如果每次啟動時都需要對其進行解析,那么為什么不可以僅在PC上對其進行解析,而不必讓用戶這樣做呢?
allSentences是ArrayList嗎? 如果是這樣,則其中的項目數量可能很多,並且必須多次調整大小。 嘗試初始化大容量的陣列。
每個ArrayList實例都有一個容量。 容量是用於在列表中存儲元素的數組的大小。 它總是至少與列表大小一樣大。 將元素添加到ArrayList后,其容量會自動增長。 除了添加元素具有固定的攤銷時間成本外,沒有指定增長策略的詳細信息。
應用程序可以使用sureCapacity操作在添加大量元素之前增加ArrayList實例的容量。 這可以減少增量重新分配的數量。 數組列表
其他人認為您可以嘗試:
從上到下,讀取字符看起來像這樣:
InputStream
讀取一個字節(通常取決於編碼); 當您讀入緩沖區時 ,會發生相同的事件序列,但是一次傳遞了數千個字節。
由此,您當然可以建立一個直覺,為什么一次從一個文件讀取一個字符很慢。
我看不到使用Pattern
and Matcher
方法有什么問題:如果表達式編寫正確,並且Patern
僅編譯一次並重用,那么它應該非常快。
您懷疑String#split
也使用一個正則表達式,並在每次調用它時對其進行重新編譯。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.