tags:

views:

292

answers:

6

Hi.

Anybody knows how to unload a file from cache? I write a file to disk, then I want to read it back. However, Windows is giving me the file from cache.

begin
...

 {-- Write file --}
 AssignFile(F, FileName);
 Rewrite(F, 1);
 BlockWrite(F, Buf[0], Chunk);
 CloseFile(F);             { FLUSH }

some code...
then.....

 {-- Read file --}
 AssignFile(F, FileName);
 Reset(F, 1);                                                              
 BlockRead(F, Buf[0], Chunk);       <----------- getting file from cache
 CloseFile(F);
end;

- I am trying to determine the write/read speed of a drive.

A: 
Suvesh Pratapa
Nope. The read speed is still 1GB/sec, which means that the file is still in memory.
Altar
@Altar: Even if the file is physically written to the hard disk, it is still in the cache from which it can be read much faster.
Andreas Hausladen
+4  A: 

I think that you have misunderstood the concept of flushing a file.

Flushing a file does not remove it from the disk cache, it causes the content of the file stream's buffer to be written to the file.

(The stream is automatically flushed when you close it. Opening a file and flushing it without writing anything to it has no effect what so ever.)

You can look into the FILE_FLAG_NO_BUFFERING flag for reading the file, but it seems from the documentation that it has no effect on files on a hard drive.

MSDN: CreateFile

Guffa
The flag does have effect for files on a hard drive as well, but it can't disable the buffering of the disc drive itself. This cache is typically in the range of 2 to 32 MByte on a modern drive, so the larger the file the smaller the influence of the drive cache will be.
mghie
My file is at least 100MB large. So I guess Windows is caching even huge files.
Altar
I think (physical) disk cache can be easily trashed if I read some other files in it. So this should not be a problems.
Altar
+2  A: 

File caching is an OS level operation, so is independent of whether you use Delphi or any other language.

If you give us some idea of why you want to ensure you are not reading from cache, it may be easier to help.

Alistair Ward
A: 

The code should be fine, unless you're using invalid values for the Chunk variable. E.g., if chunk = 0 then it won't read any data into the buffer, thus the buffer would keep it's old value. (Which could be the same data you just wrote to disk.)

Workshop Alex
No. Chunk is about 100MB.
Altar
+4  A: 

You'll need to use the Win32 API directly, specifically CreateFile with the FILE_FLAG_NO_BUFFERING flag. It forces the OS to read from the disk instead of the cache, and has the side effect that it also clears out the cache for that file, so the next read without the flag also hits the disk, though it does read it into the cache then.

Craig Peterson
+4  A: 

Some code to demonstrate the use of FILE_FLAG_NO_BUFFERING and to test how it affects your reading time:

uses
  MMSystem;

function GetTimeForRead(ABuffered: boolean): single;
const
  FileToRead = // name of file with maybe 500 MByte size
var
  FlagsAndAttributes: DWORD;
  FileHandle: THandle;
  SrcStream, DestStream: TStream;
  Ticks: DWord;
begin
  if ABuffered then
    FlagsAndAttributes := FILE_ATTRIBUTE_NORMAL
  else
    FlagsAndAttributes := FILE_FLAG_NO_BUFFERING;
  FileHandle := CreateFile(FileToRead, GENERIC_READ, FILE_SHARE_READ, nil,
    OPEN_EXISTING, FlagsAndAttributes, 0);
  if FileHandle = INVALID_HANDLE_VALUE then begin
    Result := 0.0;
    exit;
  end;

  SrcStream := THandleStream.Create(FileHandle);
  try
    DestStream := TMemoryStream.Create;
    try
      DestStream.Size := SrcStream.Size;

      Sleep(0);
      Ticks := timeGetTime;
      DestStream.CopyFrom(SrcStream, SrcStream.Size);
      Result := 0.001 * (timeGetTime - Ticks);

    finally
      DestStream.Free;
    end;
  finally
    SrcStream.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
begin
  Button1.Enabled := FALSE;
  try
    Update;
    Memo1.Lines.Clear;
    for i := 1 to 5 do begin
      Memo1.Lines.Add(Format('Time for buffered file read: %.3f s',
        [GetTimeForRead(TRUE)]));
    end;
    for i := 1 to 5 do begin
      Memo1.Lines.Add(Format('Time for unbuffered file read: %.3f s',
        [GetTimeForRead(FALSE)]));
    end;
  finally
    Button1.Enabled := TRUE;
  end;
end;

Running this code with a file of 420 MByte size gives on my system:

Time for buffered file read: 3,974 s
Time for buffered file read: 0,922 s
Time for buffered file read: 0,937 s
Time for buffered file read: 0,937 s
Time for buffered file read: 0,938 s
Time for unbuffered file read: 3,922 s
Time for unbuffered file read: 4,000 s
Time for unbuffered file read: 4,016 s
Time for unbuffered file read: 4,062 s
Time for unbuffered file read: 3,985 s

mghie
THANKS mghie!I think this is the answer I need it.Thanks a lot!
Altar
Why do you call Sleep(0) in your code?
Altar
Well, it could as well be removed. It doesn't really matter for code that takes seconds to run, but when I have code that I want to time quickly (not using an external profiler, but a profiling unit that I use) I habitually insert a "Sleep(0);" right before the start, to execute the code to be profiled within a complete time-slice. It definitely matters if you want to compare the runtime of several consecutive code snippets, each taking a few milliseconds.
mghie