Mega Code Archive

 
Categories / Delphi / Examples
 

Shotgun Copy

Title: Shotgun Copy Question: Is there a faster way to copy files across seperate physical drives? e.g. HD-HD, CDROM-HD. Using threads, we are able to issue a write at the same time as a read, effectively reading and writing files at the same time. Answer: unit Main; { ShotGun Copy by Victor Kasenda Free for any usage. DESCRIPTION ShotGun Copy will try to issue a read and write at the same time (like a double barrel shotgun). ShotGun Copy should work faster when copying files across seperate physical drives (HD-HD, CDROM-HD), as one drive can be made to read and the other drive writing data at the same time. For Hard Disks, this is theoretically faster on drives with DMA controllers, where the controller can be left alone to handle the read/write. Across the same physical drive, then the Hard disk head will be made to seek more and may have a detrimental effect on performance. The copy engine is implemented in TMainForm.ShotGunCopy 2 standard components have been added to the form: the TButton and TProgressBar. When the button is pressed, the copying is started. The progress bar provides feedback ALGORITHM 2 buffers- Buf1 and Buf2 Main Read Buf1 Main Wait for Buf2 to write finish (if Buf2 is still writing) Thread Write Buf1 Main Read Buf2 Main Wait for Buf1 to write finish (if Buf1 is still writing) Thread Write Buf2 Loop Description: Main Read - Main Thread does a read Thread Write - A TWriteThread (see below) is created to write the buffer in a tread. VARIABLES MainForm.DoingWrite if true, the write thread is in the writing process. TWriteThread will set it back to false after it has finished executing. Buf1, Buf2 the buffers BEFORE TESTING Modify the IN file path and OUT file path in the procedure ShotgunCopy. IDEAS is this thing working as it is supposed to be (reading and writing at the same time?) does the call to Synchronize(SetDoingWrite); affect performance in TWriteThread? } (**) interface (**) uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TMainForm = class(TForm) Button1: TButton; ProgressBar: TProgressBar; procedure Button1Click(Sender: TObject); private procedure WriteThreadOnTerminate(Sender: TObject); public FromF, ToF: File; DoingWrite: boolean; procedure ShotgunCopy; end; var MainForm: TMainForm; (**) implementation (**) {$R *.DFM} const BufSize = 512 * 1024; { 512 KByte buffer } type P64kBuf = ^T64kBuf; T64kBuf = array[0..1024*64-1] of char; PBigBuf = ^TBigBuf; TBigBuf = array[0..MaxLongInt-1] of char; { Create suspended } TWriteThread = class(TThread) private procedure SetDoingWrite; public BufToWrite: PBigBuf; Count: integer; // how much to write? procedure Execute; override; end; procedure TMainForm.ShotgunCopy; var WriteThread: TWriteThread; Buf1, Buf2: PBigBuf; NumRead, TotalRead, TotalSize: Integer; P: TThreadPriority; procedure UpdateProgressBar; begin ProgressBar.Position := (INT64(TotalRead) * 100) div INT64(TotalSize); end; begin P := tpHighest; {Thread priority} Screen.Cursor := crHourGlass; WriteThread := nil; GetMem(Buf1, BufSize+10); GetMem(Buf2, BufSize+10); { Modify the following two lines to assign the IN file and OUT file } AssignFile(FromF, 'e:\d2char.mpq'); {IN File} AssignFile(ToF, 'd:\temp\out.CGTest'); {OUT File} Reset(FromF, 1); Rewrite(ToF, 1); TotalRead := 0; TotalSize := FileSize(FromF); DoingWrite := false; repeat BlockRead(FromF, Buf1^, BufSize, NumRead); if NumRead = 0 then break; inc(TotalRead, NumRead); UpdateProgressBar; {Wait for Buf2 to write finish} if DoingWrite then WriteThread.WaitFor; FreeAndNil(WriteThread); {Thread write Buf1} WriteThread := TWriteThread.Create(true); with WriteThread do begin BufToWrite := Buf1; Count := NumRead; OnTerminate := MainForm.WriteThreadOnTerminate; Priority := P; end; WriteThread.Resume; {Main Read Buf2} BlockRead(FromF, Buf2^, BufSize, NumRead); if NumRead = 0 then break; inc(TotalRead, NumRead); UpdateProgressBar; {Wait for Buf2 to write finish} if DoingWrite then WriteThread.WaitFor; FreeAndNil(WriteThread); {Thread Write Buf2} WriteThread := TWriteThread.Create(true); with WriteThread do begin BufToWrite := Buf2; Count := NumRead; OnTerminate := WriteThreadOnTerminate; Priority := P; Resume; end; until false; if DoingWrite then WriteThread.WaitFor; FreeAndNil(WriteThread); Screen.Cursor := crDefault; CloseFile(FromF); CloseFile(ToF); FreeMem(Buf1); FreeMem(Buf2); end; procedure TMainForm.Button1Click(Sender: TObject); begin ShotgunCopy; end; { TWriteThread } procedure TWriteThread.Execute; begin // Write out the buffer FreeOnTerminate := false; Synchronize(SetDoingWrite); BlockWrite(MainForm.ToF, BufToWrite^, Count); end; procedure TWriteThread.SetDoingWrite; begin MainForm.DoingWrite := true; end; procedure TMainForm.WriteThreadOnTerminate(Sender: TObject); begin DoingWrite := false; end; end.