2014-03-19 33 views
1

我試圖找到一種方式,可以最快的方式來複制大文件...以最快的方式複製大文件可能

import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.util.ArrayList; 

public class FastFileCopy { 


public static void main(String[] args) { 
    try { 
     String from = "..."; 
     String to = "..."; 
     FileInputStream fis = new FileInputStream(from); 
     FileOutputStream fos = new FileOutputStream(to); 
     ArrayList<Transfer> transfers = new ArrayList<>(); 
     long position = 0, estimate; 
     int count = 1024 * 64; 
     boolean lastChunk = false; 
     while (true) { 
      if (position + count < fis.getChannel().size()) { 
       transfers.add(new Transfer(fis, fos, position, position + count)); 
       position += count + 1; 
       estimate = position + count; 
       if (estimate >= fis.getChannel().size()) { 
        lastChunk = true; 
       } 
      } else { 
       lastChunk = true; 
      } 
      if (lastChunk) { 
       transfers.add(new Transfer(fis, fos, position, fis.getChannel().size())); 
       break; 
      } 
     } 
     for (Transfer transfer : transfers) { 
      transfer.start(); 
     } 
    } catch (IOException ex) { 
     ex.printStackTrace(); 
    } 
} 

} 

然後創建這個類:

import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.nio.channels.FileChannel; 

public class Transfer extends Thread { 

private FileChannel inChannel = null; 
private FileChannel outChannel = null; 
private long position, count; 

public Transfer(FileInputStream fis, FileOutputStream fos, long position, long count) { 
    this.position = position; 
    this.count = count; 
    inChannel = fis.getChannel(); 
    outChannel = fos.getChannel(); 
} 

@Override 
public void run() { 
    try { 
     inChannel.transferTo(position, count, outChannel); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

} 

我測試了它和結果是非常非常令人印象深刻的... 但有一個大問題,複製的文件比當前文件大veryyyyy!

的話,請檢查一下,幫我找到了問題,謝謝:))

+4

'Files.copy(source,destination)'對你來說不夠快嗎?另外,如果文件位於單個硬盤上,使用多個線程將會降低***性能。 – assylias

+0

否:))...你可以用這種方式在20秒內複製3GB – AvB

+0

你至少有_tried_ Files Files()嗎? – fge

回答

2

由於每個循環中,您增加通過計數+ 1的位置,以及你與`(FIS,FOS,位置,位置+計數)傳輸時,你的代碼將創建傳輸對象如下:

new Transfer(fis, fos, 0,count) 
new Transfer(fis, fos, count+1, 2count+1) 
new Transfer(fis, fos, 2count+2, 3count+2) 
new Transfer(fis, fos, 3count+3, 4count+3) 
... 

因此,雖然您將創建filesize/count傳輸類,但您要求總共傳輸(count + 1) * (1 + 2 + 3 + ...)個字節。

此外,我不認爲FileChannel.TransferTo()以您認爲的方式工作。 position指定開始閱讀的源文件中的位置。它沒有指定您在目標頻道中寫入的位置。所以,即使你的尺寸正確,你最終會得到正確尺寸的輸出文件,但內容將以線程寫入它們的順序混亂。您可以撥打outChannel.position()跳到正確的位置。我不清楚什麼樣的混亂可能會發生,因爲多個線程以這種方式擴展文件大小。


實驗很好,我鼓勵你試試這個和基準。然而,評論是正確的,這種方法是錯誤的。只有一個磁盤,只有一個文件系統緩衝區支持,並有多個線程爭奪它不會使其工作更快 - 並可能使其變慢。

你就不可能改善:

long count = 0; 
long size = src.size(); 
while(count < size) { 
    count += src.transferTo(count, size - count, dest); 
} 

也確實注意到,這是非常困難的有關文件操作的性能判斷,因爲文件系統會緩存讀取和寫入,這樣一個可怕的很多你做的只是在RAM上超便宜的操作。

另請注意,至少在進行基準測試時,在考慮複製完成之前,您將需要join()以及您開始的所有線程。

+0

非常感謝你:) – AvB

5

這是一個XY問題。只需使用Files.copy()

看那個,看看這是不是你不夠快:

$ ls -lh ~/ubuntu-13.04-desktop-amd64.iso 
-rw-rw-r-- 1 fge fge 785M Jul 12 2013 /home/fge/ubuntu-13.04-desktop-amd64.iso 
$ cat Foo.java 
import java.io.IOException; 
import java.nio.file.Files; 
import java.nio.file.Paths; 
import java.nio.file.StandardCopyOption; 

public class Foo 
{ 
    public static void main(final String... args) 
     throws IOException 
    { 
     Files.copy(Paths.get("/home/fge/ubuntu-13.04-desktop-amd64.iso"), 
      Paths.get("/tmp/t.iso"), StandardCopyOption.REPLACE_EXISTING); 
    } 
} 
$ time java Foo 

real 0m1.860s 
user 0m0.077s 
sys 0m0.648s 
$ time java Foo 

real 0m1.851s 
user 0m0.101s 
sys 0m0.598s 

它可能是更快。上帝知道爲什麼,Oracle不使用sendfile(2),即使這是Java 8並且Linux 2.2在這裏已經有相當長的一段時間了。

+1

在任何有自尊心的開發人員的機器上,這個程序會將文件緩存在內存中,操作系統將在程序結束後寫回髒的FS緩存*。在progream執行後添加時間同步並添加時間。 –

+0

@fge謝謝:) – AvB

相關問題