2012-04-12 67 views
3

我有兩個包含數百萬條目的數組(int和long)。到現在爲止,我正在使用DataOutputStream並使用長緩衝區,因此磁盤I/O成本變得很低(因爲Iio具有巨大的緩衝區,因此I/O訪問成本較低),具體來說,使用Java Array大容量刷新磁盤

DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("abc.txt"),1024*1024*100)); 

for(int i = 0 ; i < 220000000 ; i++){ 
    long l = longarray[i]; 
    dos.writeLong(l); 
} 

但是這需要幾秒鐘(超過5分鐘)。其實,我想批量刷新(某種主內存到磁盤內存映射)。爲此,我在herehere中發現了一個很好的方法。但是,無法理解如何在我的javac中使用它。任何人都可以幫助我解決這個問題或者其他方法來做到這一點嗎?

+1

這真的不會超過幾秒鐘。我會嘗試將緩衝區大小減小到32 KB。大於此的緩衝區實際上可能會變慢。根據您的硬件,您應該可以寫入至少40至400 MB/s。 – 2012-04-12 16:11:21

+0

@PeterLawrey,我修改我的代碼。它有什麼問題嗎?但是,它需要498秒。 – Arpssss 2012-04-12 16:18:59

+0

你正在寫本地文件系統(而不是網絡)嗎? – NPE 2012-04-12 16:24:58

回答

2

在我的機器,3.8GHz的酷睿i7與SSD

DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("abc.txt"), 32 * 1024)); 

long start = System.nanoTime(); 
final int count = 220000000; 
for (int i = 0; i < count; i++) { 
    long l = i; 
    dos.writeLong(l); 
} 
dos.close(); 
long time = System.nanoTime() - start; 
System.out.printf("Took %.3f seconds to write %,d longs%n", 
     time/1e9, count); 

打印

Took 11.706 seconds to write 220,000,000 longs 

使用內存映射在我的3.8 GHz的文件

final int count = 220000000; 

final FileChannel channel = new RandomAccessFile("abc.txt", "rw").getChannel(); 
MappedByteBuffer mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, count * 8); 
mbb.order(ByteOrder.nativeOrder()); 

long start = System.nanoTime(); 
for (int i = 0; i < count; i++) { 
    long l = i; 
    mbb.putLong(l); 
} 
channel.close(); 
long time = System.nanoTime() - start; 
System.out.printf("Took %.3f seconds to write %,d longs%n", 
     time/1e9, count); 

// Only works on Sun/HotSpot/OpenJDK to deallocate buffer. 
((DirectBuffer) mbb).cleaner().clean(); 

final FileChannel channel2 = new RandomAccessFile("abc.txt", "r").getChannel(); 
MappedByteBuffer mbb2 = channel2.map(FileChannel.MapMode.READ_ONLY, 0, channel2.size()); 
mbb2.order(ByteOrder.nativeOrder()); 
assert mbb2.remaining() == count * 8; 
long start2 = System.nanoTime(); 
for (int i = 0; i < count; i++) { 
    long l = mbb2.getLong(); 
    if (i != l) 
     throw new AssertionError("Expected "+i+" but got "+l); 
} 
channel.close(); 
long time2 = System.nanoTime() - start2; 
System.out.printf("Took %.3f seconds to read %,d longs%n", 
     time2/1e9, count); 

// Only works on Sun/HotSpot/OpenJDK to deallocate buffer. 
((DirectBuffer) mbb2).cleaner().clean(); 

打印I7。

Took 0.568 seconds to write 220,000,000 longs 

上一個較慢的機器打印

Took 1.180 seconds to write 220,000,000 longs 
Took 0.990 seconds to read 220,000,000 longs 

這裏是任何其他方式無法創建?因爲我的主內存上已經有了這個數組,我不能分配超過500 MB的空間來完成這個任務?

這不會使用小於1 KB的堆。如果你看看這次調用之前和之後使用了多少內存,通常你根本看不到增加。

另一件事,這是否給出了高效加載還意味着MappedByteBuffer?

根據我的經驗,使用內存映射文件是迄今爲止最快的,因爲您可以減少系統調用次數並將其複製到內存中。

因爲在一些文章中我發現讀取(緩衝區),這可以提供更好的加載性能。 (我檢查一個,真的更快2.2億int array -float數組讀取5秒)

我想閱讀那篇文章,因爲我從來沒有見過。

另一個問題:readLong給出錯誤而從代碼輸出文件中讀取證法的表現

一部分存儲在本地字節順序的值。 writeLong/readLong總是使用big endian格式,這在英特爾/ AMD系統上本來就是小端格式。

可以使字節順序大端這將慢下來,或者您可以使用本機排序(的DataInput/OutputStream中只支持大端)

+0

再次感謝。問題是,我必須使用以下命令分配另一個相同大小的數組(2.64 GB,我有另一個int數,大小相同):map(FileChannel.MapMode.READ_WRITE,0,count * 8)。這是否有其他方式不創造這個?因爲我的主內存上已經有了這個數組,我不能分配超過500 MB的空間來完成這個任務?另一件事,這是否給了高效的加載也意味着MappedByteBuffer?因爲在一些文章中我發現讀取(緩衝區),這可以提供更好的加載性能。 (我檢查一個,真的更快2.2億個int數組 - 浮點數組讀取5秒)。 – Arpssss 2012-04-13 01:36:07

+0

另一個問題:readLong在從您的代碼輸出文件中讀取時出錯。 – Arpssss 2012-04-13 01:36:43

+0

我回復了你的評論作爲編輯。順便說一句:如果你只使用MappedByteBuffer而不是int []或long []或double [],它根本不會使用太多的堆(並且你不需要做任何事情來將數據寫入磁盤) – 2012-04-13 08:11:01

1

我與16GB內存,2.13 GHz的[CPU]

運行它的服務器我懷疑這個問題有什麼用Java代碼。

您的文件系統顯得非常慢(至少比本地磁盤所期望的慢十倍)。

我會做兩件事情:

  1. 仔細檢查您的實際寫入到本地磁盤,而不是網絡共享。請記住,在某些環境中,主目錄是NFS掛載。
  2. 要求系統管理員查看機器,找出磁盤爲何如此緩慢。如果我在他們的鞋子裏,我會先檢查日誌並運行一些基準測試(例如使用Bonnie++)。
+0

謝謝,我會檢查。這似乎在系統中有缺陷。我必須問。謝謝。 – Arpssss 2012-04-12 16:52:41