Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Как лучше работать с 300-мегабайтным массивом ? [D7, WinME, WinXP]


Страницы:   1 2 3 4

Алекс А ©   (2004-06-21 02:45:00) 

Есть файл на диске "base1.bin" размером в 300 мегабайт, его надо загрузить в массив Base1 . Делают так :

 NewFile := TFileStream.Create('base1.bin', fmOpenRead or fmShareDenyWrite) ;
 try
   NewFile.ReadBuffer(Base1, 300000000);
 finally
   NewFile.Free;
 end;


Данные используются только для чтения. Загрузка длится более минуты. Хотелось бы, чтобы загрузка шла не сразу от и до, а как то постепенно, по мере обращения программы к отдельным частям массива, чтобы небыло этой минутной паузы. Можно ли это сделать ? Или как вообще лучше это организовать ?


jack128   (2004-06-22 12:18:00) [21]


> CopyFrom на каждой итерации буде вызывать дорогое (и стающее
> всё более дорогим) перераспределение памяти...
> самый простой метод лечения - MemoryStream.Size := FileStream.Size
> до копирования...

в СopyFrom так и сделано.. можно узнать, а как ты проверял, что у тя файл в ОЗУ загрузился?

у мя такой вот тестик с 300 метровым файлом 1,5 минуты длился..
procedure TForm1.Button1Click(Sender: TObject);
var
 p: Pointer;
 hFile: THandle;
 fSize: Cardinal;
 t, Dummy: Cardinal;
begin
 if not od.Execute then Exit;
 t := GetTickCount;
 hFile := CreateFile(PChar(od.Filename), GENERIC_READ, 0, nil,
   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
 if hFile = INVALID_HANDLE_VALUE then
   RaiseLastWin32Error;
 try
   fSize := SetFilePointer(hFile, 0, nil, FILE_END);
   GetMem(p, fSize);
   try
     SetFilePointer(hFile, 0, nil, FILE_BEGIN);
     if not ReadFile(hFile, p^, fSize, dummy, nil) then
       RaiseLastWin32Error;
   finally
     FreeMem(p);
   end;
 finally
   CloseHandle(hFile);
 end;
 Caption := IntToStr(GetTickCount - t);
end;


wicked   (2004-06-22 11:59:00) [22]


> а есть предположения почему MemoryStream.CopyFrom(FileStream,
> FileStream.Size) происходит минуту?

есть...
1. CopyFrom работает на уровне TStream и копирует содержимое блоками по 60 кб (примерно, точное число в исходниках)...
2. TMemoryStream при необходимости увеличить место под данные просто перевыделяет память через GlobalAlloc/GlobalFree... при этом, чем больше памяти было выделено до операции, тем больше времени займет само перераспределение...
т. о. CopyFrom на каждой итерации буде вызывать дорогое (и стающее всё более дорогим) перераспределение памяти...
самый простой метод лечения - MemoryStream.Size := FileStream.Size до копирования...


jack128   (2004-06-22 11:52:00) [23]

а есть предположения почему MemoryStream.CopyFrom(FileStream, FileStream.Size) происходит минуту?


wicked   (2004-06-22 11:42:00) [24]


> А в форуме несколько раз проскакивало, что таким образом
> можно ускорить доступ к файлу. Может кто из сведущих прояснит
> ситуацию?

может... 400 мб файл с собственным форматом данных загружается за полсекунды-секунду... это вместе с предобработкой (создание таблиц в памяти)...


Digitman   (2004-06-22 11:41:00) [25]


> jack128 ©   (22.06.04 11:32) [41]


структурированную инф-цию такогообъема, разумеется, в БД храню.. а почему бы и нет ? если есть готовый достаточно производительный механизм быстрого доступа к элементам НД ?


jack128   (2004-06-22 11:32:00) [26]


>  Конечно на произвольном доступе немного отстает, но за
> счет опережающего чтения во многих случаях быстрее. Но это
> не главная характеристика
главная характеристика в ДАННОМ случае - скорость произвольного доступа. По крайней мере так автор сказал.

> Digitman ©   (22.06.04 08:57)
> а нашута велосипед изобретать ?

Скажите, вы всю информацию в своих форматах в БД храните?

И насчет мапирования:
Может я чего то не понимаю, но AFAIK при использовании мапирования в ОЗУ нечего не загружается.  Поэтому скорость доступа никак не изменяет, это лишь вопрос удобства, не больше. А в форуме несколько раз проскакивало, что таким образом можно ускорить доступ к файлу. Может кто из сведущих прояснит ситуацию?


wicked   (2004-06-22 11:02:00) [27]

упс... а куда ушло форматирование?... :(


wicked   (2004-06-22 11:01:00) [28]

unit wacStreams;

interface
uses Windows, SysUtils, Classes;

{$WEAKPACKAGEUNIT ON}

const
def_Granularity = (512 * 1024);

type
TMemoryMapStream = class(TCustomMemoryStream)
protected
 fRealSize: integer;
 fGranularity: integer;
 fFile: THandle;
 fMemMap: THandle;
 fReadOnly: boolean;

 procedure SetSize(const NewSize: int64); overload; override;
 procedure SetGranularity(value: integer); virtual;

 procedure OpenMMap; virtual;
 procedure CloseMMap; virtual;
 procedure Map; virtual;
 procedure UnMap; virtual;
 procedure doPack(reopen: boolean); virtual;
 procedure SetEOF(pos: integer); virtual;

public
 constructor Create(const AFileName: string; RequestReadOnly: boolean = false; AGranularity: integer = def_Granularity);
 destructor Destroy; override;

 procedure Clear;
 procedure SetSize(NewSize: integer); overload; override;
 function Write(const Buffer; Count: longint): longint; override;

 procedure LoadFromFile(const FileName: string); virtual;
 procedure LoadFromStream(Stream: TStream); virtual;
 procedure Pack; virtual;

 property Granularity: integer read fGranularity write SetGranularity;
 property ReadOnly: boolean read fReadOnly;
end;

implementation

const
min_size = 1024;

constructor TMemoryMapStream.Create(const AFileName: string; RequestReadOnly: boolean = false; AGranularity: integer = def_Granularity);
var filesize: integer;
begin
inherited Create;
SetPointer(nil, 0);
fRealSize:= 0;
SetGranularity(AGranularity);
fFile := 0;
fMemMap := 0;
fReadOnly := false;
try
 if not RequestReadOnly then
  fFile := CreateFile(
   PChar(AFileName),         // name
   GENERIC_READ or GENERIC_WRITE,      // desired access
   FILE_SHARE_READ,         // share mode (share read)
   nil,            // security attributes
   OPEN_ALWAYS,          // OPEN_EXISTING, // creation disposition
   FILE_ATTRIBUTE_NORMAL or FILE_FLAG_RANDOM_ACCESS, // flags and attributes
   0{nil});           // template file
 if (fFile = INVALID_HANDLE_VALUE) or RequestReadOnly then begin
  fFile := CreateFile(
   PChar(AFileName),         // name
   GENERIC_READ,          // desired access
   FILE_SHARE_READ,         // share mode (share read)
   nil,            // security attributes
   OPEN_EXISTING,          // creation disposition
   FILE_ATTRIBUTE_NORMAL or FILE_FLAG_RANDOM_ACCESS, // flags and attributes
   0{nil});           // template file
  if fFile = INVALID_HANDLE_VALUE then raise Exception.Create('could not open the file ' + AFileName);
  fReadOnly := true;
    end;
    filesize := GetFileSize(fFile, nil);
 if filesize = 0 then SetEOF(min_size);
    OpenMMap;
 except
CloseMMap;
   if (fFile <> 0) and (fFile <> INVALID_HANDLE_VALUE) then CloseHandle(fFile);
   raise;
 end;
 Seek(0, soFromBeginning);
end;

destructor TMemoryMapStream.Destroy;
begin
if fFile <> 0 then begin
    doPack(false);
    CloseHandle(fFile);
   end;
end;

procedure TMemoryMapStream.OpenMMap;
begin
if fFile <> 0 then begin
 if fReadOnly then fMemMap := CreateFileMapping(fFile, nil, PAGE_READONLY or SEC_COMMIT, 0, 0, nil)
 else fMemMap := CreateFileMapping(fFile, nil, PAGE_READWRITE or SEC_COMMIT, 0, 0, nil);
 if fMemMap = 0 then raise Exception.Create('could not create memory map of file');
 Map;
end;
end;

procedure TMemoryMapStream.CloseMMap;
begin
if fMemMap <> 0 then begin
       UnMap;
       CloseHandle(fMemMap);
end;
end;

procedure TMemoryMapStream.Map;
var p: pointer;
filesize: integer;
begin
   SetPointer(nil, 0);
   if fReadOnly then
       p := MapViewOfFile(fMemMap, FILE_MAP_READ, 0, 0, 0)
else
 p := MapViewOfFile(fMemMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if p = nil then raise Exception.Create('could not read file');
filesize := GetFileSize(fFile, nil);
fRealSize := filesize;
SetPointer(p, filesize);
Position := 0;
end;

procedure TMemoryMapStream.UnMap;
begin
if Memory <> nil then begin
 if not fReadOnly then FlushViewOfFile(Memory, 0);
 UnmapViewOfFile(Memory);
end;
end;

procedure TMemoryMapStream.doPack(reopen: boolean);
begin
CloseMMap;
try
 SetEOF(Size);
   finally
    if reopen then OpenMMap;
end;
end;

procedure TMemoryMapStream.SetEOF(pos: integer);
begin
   if fReadOnly then exit;
   if SetFilePointer(fFile, pos, nil, FILE_BEGIN) = DWORD(-1) then // !!! INVALID_SET_FILE_POINTER
    raise Exception.Create('could not resize file');
   SetEndOfFile(fFile);
end;

//---------------------------------------------------------------------------

procedure TMemoryMapStream.SetSize(const NewSize: int64);
begin
SetSize(integer(NewSize));
end;

procedure TMemoryMapStream.SetGranularity(value: integer);
begin
if value = fGranularity then exit;
   fGranularity := value;
   if fGranularity < min_size then fGranularity := min_size;
end;

procedure TMemoryMapStream.Clear;
begin
   SetSize(min_size);
   doPack(true);
   ZeroMemory(Memory, min_size);
end;

procedure TMemoryMapStream.SetSize(NewSize: integer);
var mult: integer;
begin
if NewSize = Size then exit;
if (fFile <> 0) and not fReadOnly then begin
 if NewSize > fRealSize then begin
  CloseMMap;
  mult := (NewSize - fRealSize) div fGranularity;
           if ((NewSize - fRealSize) mod fGranularity) > 0 then
            fRealSize := fRealSize + ((mult + 1) * fGranularity)
  else
            fRealSize := fRealSize + (mult * fGranularity);
  SetEOF(fRealSize);
           OpenMMap;
  SetPointer(Memory, NewSize);
 end
 else SetPointer(Memory, NewSize);
end;
end;

function TMemoryMapStream.Write(const Buffer; Count: longint): longint;
var pos: longint;
begin
pos := Position;
if (pos < 0) or (Count <= 0) or fReadOnly then begin
 result := 0;
 exit;
   end;
if (pos + Count) > Size then SetSize(pos + Count);
System.Move(Buffer, (pointer(longint(Memory) + pos))^, Count);
pos := pos + Count;
Position := pos;
   Result := Count;
end;

procedure TMemoryMapStream.LoadFromFile(const FileName: string);
var f: TFileStream;
begin
   f := TFileStream.Create(FileName, fmOpenRead);
   try
    LoadFromStream(f);
finally
    f.Free;
end;
end;

procedure TMemoryMapStream.LoadFromStream(Stream: TStream);
var sz: integer;
begin
if fReadOnly then exit;
sz := Stream.Size;
SetSize(sz);
Stream.Read(Memory^, sz);
Seek(0, soFromBeginning);
end;

procedure TMemoryMapStream.Pack;
begin
doPack(true);
end;

end.


wicked   (2004-06-22 11:01:00) [29]

> Алекс А
memory mapped file - как раз то, что Вам нужно.... главное - не стремиться зарузить сразу все 300 мб файла в память...
в следующем моём постинге - код модуля с классом, наподобие TMemoryStream, только работает с memory mapped file...


Igorek   (2004-06-22 10:45:00) [30]


> Digitman ©   (22.06.04 08:57) [36]

В целях обучения.


Digitman   (2004-06-22 08:57:00) [31]


> Anatoly Podgoretsky ©   (22.06.04 08:45) [35]


действительно, зачем изобретать собственный контейнерный формат и движок к нему, когда есть тот же DBF-формат и, например, CodeBase-движок к нему ? Если уж использование BDE (прямо перед носом !) претит ?


Anatoly Podgoretsky   (2004-06-22 08:45:00) [32]

Так как речь идет о файловых вещах, то базы типа дБейс по сути почти типизированая вещь, записи фиксированой длины, определенной структуры + заголовок. Конечно на произвольном доступе немного отстает, но за счет опережающего чтения во многих случаях быстрее. Но это не главная характеристика. А главное то, что отметил Digitman ©   (22.06.04 08:25) [34]


Digitman   (2004-06-22 08:25:00) [33]


> jack128 ©   (21.06.04 20:09) [27]
> когда это БД была быстрее типизированного файла?


а нашута велосипед изобретать ?

большинство СУБД, может, и проигрывает незначительно в производительности движка, но зато дает готовый и удобный интерфейс для позиционирования и выборки ..


Almaz   (2004-06-22 04:55:00) [34]


> Боюсь, что слишком гиморойно будет.
> А нет ли какого нибудь менеджера памяти, который бы это
> сам делал? Чтобы просто проассоциировать массив с файлом
> на диске, и Дэльфи как нибудь сама бы это подгружала по
> мере необходимости, буферизировала бы, и всю прочую рутину
> сама бы выполняла?

Это не менеджер памяти - это Вы очень точно описали тот самый MemoryMapping про который Вам уже не однократно говорили.
Вот простейший пример использования:
var
 MyArray: PChar;
 FileHandle: THandle;
 FileMap: THandle;
begin
 FileHandle := CreateFile('base1.bin', GENERIC_READ, 0, nil,
   OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
 if FileHandle <> INVALID_HANDLE_VALUE then
 begin
   FileMap := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
   if FileMap <> 0 then
   begin
     MyArray := MapViewOfFile(FileMap, FILE_MAP_READ, 0, 0, 0);
     if Assigned(MyArray) then
     try
      ...
       // Здесь можете работать с MyArray как с обычным массивом
       // только Read-only, как и было указано в задаче
       // все остальное возьмет на себя система.
       // ТОЛЬКО НЕ МЕНЯЙТЕ ЗНАЧЕНИЕ ПЕРЕМЕННОЙ MyArray

       // Пример использования:
       ShowMessage(MyArray[10245] + MyArray[1008874]);

      ...
     finally
       UnmapViewOfFile(MyArray);
       CloseHandle(FileMap);
       CloseHandle(FileHandle);
     end;
   end;
 end;
end;


Удачи.


Алекс А   (2004-06-22 02:25:00) [35]


> Fay   (21.06.04 08:12) [22]
> Таких задач не бывает - "надо миллион раз в секунду кидаться
> в произвольное место 300-мегабайтного массива (чего-то?)".


Зачем передёргиваете? Не в секунду, а в минуту Если вы лично не сталкивались с задачами, когда надо делать очень много проверок по базе, то это не значит, что таких задач несуществует.


> Романов Р.В. ©   (21.06.04 08:38) [23]
> Очень простое решение. Доставай нужную информацию прямо
> из файла с помощью CreateFileMapping или TFileStream. О
> производительности позаботятся кэш windows и HDD

Нет ли каких нибудь демок с этими FileMappingом?


> MacroDenS ©   (21.06.04 20:05) [26]
> Сделай что-то вроде метрового кэна, то что чаше загружается
> пусть сразу при запуске грузится, а остальное по мере необходимости
> пусть подгружается...

Что чаще загружается - никогда заранее не известно.


> Igorek ©   (21.06.04 21:37) [28]
> 1) разбей файл на блоки (эмпиричеки определи принцип)
> 2) добавь к этому файлу еще один - файл приоритета загрузки
> - когда программа грузится, загружай только блоки, которые
> прописаны в файле приоритетов
> 3) при выходе из программы перестраивай файл приоритета
> в соотв. с протоколом работы с исходным файлом

Овчинка выделки не стоит. Городить такой огород, чтобы слегка ускорить загрузку. Я лучше подожду минутку пока загрузится.


> SergP ©   (21.06.04 23:50) [31]
> Ну а если так: При запуске проги ничего не загружать.
> Разбиваем файл на несколько блоков (допустим как предлагали
> - мегабайтные блоки, или даже поменьше...)...
> Если проге захотелось каких-то данных

Ей это будет хотеться миллион раз в минуту. Добавить миллион проверок - значит добавить тормозов, что нежелательно.


SergP   (2004-06-21 23:50:00) [36]

Ну а если так: При запуске проги ничего не загружать.
Разбиваем файл на несколько блоков (допустим как предлагали - мегабайтные блоки, или даже поменьше...)...
Если проге захотелось каких-то данных - определяем в каком они блоке и загружен ли в данный момент этот блок. Если они были  загружены ранее, если да используем то что загружено, если нет то загружаем этот блок...
?


jack128   (2004-06-21 22:58:00) [37]


> Практически всегда когда необходимо отфильтровать данные,
> сделать определенную выборку

гм. А автору это нужно? Пока что автор просит помочь закачать в память 300 метров, чтоб при этом интерфейс не тормозил ;-)

> ну и в конце концов чем DBF нетипизированный файл?
я не работал с ними, не знаю формата..


Palladin   (2004-06-21 22:35:00) [38]


>  jack128 ©   (21.06.04 20:09)

Практически всегда когда необходимо отфильтровать данные, сделать определенную выборку при этом не съедая сотни мегабайт памяти и не заморачиваясь на технических решениях, сотнями раз реализованных другими...
ну и в конце концов чем DBF нетипизированный файл?


Igorek   (2004-06-21 21:37:00) [39]

1) разбей файл на блоки (эмпиричеки определи принцип)
2) добавь к этому файлу еще один - файл приоритета загрузки - когда программа грузится, загружай только блоки, которые прописаны в файле приоритетов
3) при выходе из программы перестраивай файл приоритета в соотв. с протоколом работы с исходным файлом


jack128   (2004-06-21 20:09:00) [40]


> Digitman ©   (21.06.04 09:26)

когда это БД была быстрее типизированного файла?

> Алекс А  

ты прав, простого решения ДЛЯ НАС не существует, мы не знаем специфику задачи...


Страницы:   1 2 3 4

Наверх

Титульная страница Поиск, карта сайта Написать письмо