Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.07.11;
Скачать: CL | DM;

Вниз

Как лучше работать с 300-мегабайтным массивом ?   Найти похожие ветки 

 
Алекс А   (2004-06-21 02:45) [0]

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

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


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


 
default ©   (2004-06-21 02:57) [1]

"Можно ли это сделать ? Или как вообще лучше это организовать ?"
можно, для того чтобы сказать как лучше нужно больше информации


 
Fay   (2004-06-21 03:20) [2]

Он тебе весь в памяти нужен? Хоть раз. FileSeek (или SetFilePointer) не спасёт отца русской демократии?


 
default ©   (2004-06-21 03:25) [3]

Fay   (21.06.04 03:20) [2]
отца бы спосло


 
Алекс А   (2004-06-21 03:27) [4]


> Fay   (21.06.04 03:20) [2]
> Он тебе весь в памяти нужен? Хоть раз. FileSeek (или SetFilePointer)
> не спасёт отца русской демократии?

Примерно миллион чтений различных элементов в минуту. Правда не в одном месте, а больше по определённым небольшим областям. разбросанным по всему массиву.


 
Алекс А   (2004-06-21 03:33) [5]


> Fay   (21.06.04 03:20) [2]
> Он тебе весь в памяти нужен?

Я так подумал. Пожалуй весь он никогда не бывает нужен. Только частями, но которые находятся где попало.


 
Fay   (2004-06-21 03:39) [6]

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


 
Fay   (2004-06-21 03:43) [7]

Можно зрузить мегабайтными () блоками и хранить массив с инфой о том, какие блоки уже загружены. А грузит лучше через CreateFile/SetFilePointer/ReadFile.


 
Алекс А   (2004-06-21 03:50) [8]


> Fay   (21.06.04 03:43) [7]
> Можно зрузить мегабайтными () блоками и хранить массив с
> инфой о том, какие блоки уже загружены. А грузит лучше через
> CreateFile/SetFilePointer/ReadFile.

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


 
Fay   (2004-06-21 03:53) [9]

CreateFileMapping ку?


 
Fay   (2004-06-21 03:55) [10]

Не, точно не ку 8)


 
Fay   (2004-06-21 03:59) [11]

В Delphi традиционно нет средств, работающик "как нибудь".
Всё работает совершенно конкретным образом.


> и всю прочую рутину сама бы выполняла


1) Так не бывает 8)
2) А зачем тогда ты?!


 
ЮЮ ©   (2004-06-21 04:05) [12]

А на компе памяти сколько? Не приводит ли такое считывание "в память" к записи в своп?


 
Fay   (2004-06-21 04:15) [13]

... ёжик задумался...
8)


 
Алекс А   (2004-06-21 04:20) [14]


> ЮЮ ©   (21.06.04 04:05) [12]
> А на компе памяти сколько? Не приводит ли такое считывание
> "в память" к записи в своп?

512 Мб. Не приводит. Но и не кэшируется. При повторном запуске приходится опять ждать минуту.


> Fay   (21.06.04 03:53) [9]
> CreateFileMapping ку?

Нашёл статью об этом : http://www.delphiworld.narod.ru/base/file_of_sop.html

Про него написано :
* TSharedStream работает правильно только с файлом подкачки,
  с обычным файлом проще и надежнее работать TFileStream"ом.

* Для тех кто знаком с File Mapping Functions"ами :
    Класс TSharedStream не может использоваться для синхронизации(разделения)
    данных среди различных процессов(программ/приложений). [пояснения в конструкторе]

* Класс TSharedStream можно рассматривать как альтернативу
  временным файлам (т.е. как замену TFileStream).
  Преимущество :
    а. Данные никто не сможет просмотреть.
    б. Страница, зарезервированная под данные, автомотически освобождается
       после уничтожения создавшего ее TSharedStream"а.

* Класс TSharedStream можно рассматривать как альтернативу
  TMemoryStream.
  Преимущество :
    а. Не надо опасаться нехватки памяти при большом объеме записываемых данных.
       [случай когда физически нехватает места на диске здесь не рассматривается].


И что это даёт ? Я и так не опасаюсь нехватки места на диске ...


 
Алекс А   (2004-06-21 04:35) [15]


> Fay   (21.06.04 03:59) [11]
> > и всю прочую рутину сама бы выполняла
>
> 1) Так не бывает 8)

Бывает, когда кто нибудь напишет. Пример - сама Виндос, которая кэширует запись/чтение на диск, причём так, что для пользователя это можно сказать незаметно.


> 2) А зачем тогда ты?!

Чтобы данные анализировать.


 
Fay   (2004-06-21 04:36) [16]

При повторном запуске чего?


 
Алекс А   (2004-06-21 04:46) [17]

procedure TSharedStream.LoadFromFile(const FileName: string);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
  LoadFromStream(Stream)
finally
  Stream.Free
end
end;


Это же мой код! Вон я в первом постинге его написал. Просто один в один. Стырили. :)
В общем как я понял SharedStream позволяет просто грузить массив по частям, причём чего загружено, а чего нет, думать самому. Слишком неудобно это как-то.


 
Алекс А   (2004-06-21 04:48) [18]


> Fay   (21.06.04 04:36) [16]
> При повторном запуске чего?

Программы конечно.


 
Fay   (2004-06-21 04:50) [19]


> Пример - сама Виндос, которая кэширует запись/чтение на
> диск, причём так, что для пользователя это можно сказать
> незаметно.

А что тебе говорит мозг, кеширует "Виндос" 300 метров, или нет?

Снова спрошу
При повторно запуске ЧЕГО надо избежать минутной задержки?!


 
Fay   (2004-06-21 04:53) [20]

Всё, уже вижу. 8)
А значения локальных переменных процедур "кешировать" не надо?!!!
Какой-то...
Удачи.


 
Алекс А   (2004-06-21 05:03) [21]

Ну, в общем я понял. Задача не имеет простого решения. А делать сложные я небуду.


 
Fay   (2004-06-21 08:12) [22]

Таких задач не бывает - "надо миллион раз в секунду кидаться в произвольное место 300-мегабайтного массива (чего-то?)".
Что на самом деле надо сделать, раз ты дошёл до такой жизни?


 
Романов Р.В. ©   (2004-06-21 08:38) [23]

Очень простое решение. Доставай нужную информацию прямо из файла с помощью CreateFileMapping или TFileStream. О производительности позаботятся кэш windows и HDD


 
Digitman ©   (2004-06-21 09:26) [24]


> Алекс А   (21.06.04 05:03) [21]
> Задача не имеет простого решения. А
> делать сложные я небуду.


Имеет.

Решение в таких случаях - отказ от собственного контейнера данных и движка управления ими в пользу одной из готовых существующих СУБД. Любая сколь-либо серьезная СУБД имеет готовые механизмы и интерфейс быстрого индексированного доступа к записям в таблицах БД любого размера, при этом таблица не считывается целиком в память, образуя набор данных, а считывается только позиционируемый фрагмент (одна или несколько последовательно идущих записей)


 
ChainikDenis ©   (2004-06-21 19:56) [25]

А ведь можно сразу к контроллера диска обращаться и считывать сектора - это ваще класно будет. Занял под файл определенные сектора, и читаешь какие только нужны..


 
MacroDenS ©   (2004-06-21 20:05) [26]

А вобще какой смысл так напрягать память и хард?
На кой тебе ворочить сразу 300 метров?
Лучше все же по чуть-чуть,
потому как ну загрузишь ты (пусть даже быстро) в память все 300 метров, а из памяти она в файл подкачки полезет...
Сделай что-то вроде метрового кэна, то что чаше загружается пусть сразу при запуске грузится, а остальное по мере необходимости пусть подгружается...
если ты за быстрый поиск бьешься, то здесь ты особо много не выиграешь...


 
jack128 ©   (2004-06-21 20:09) [27]


> Digitman ©   (21.06.04 09:26)

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

> Алекс А  

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


 
Igorek ©   (2004-06-21 21:37) [28]

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


 
Palladin ©   (2004-06-21 22:35) [29]


>  jack128 ©   (21.06.04 20:09)

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


 
jack128 ©   (2004-06-21 22:58) [30]


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

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

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


 
SergP ©   (2004-06-21 23:50) [31]

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


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


> 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]
> Ну а если так: При запуске проги ничего не загружать.
> Разбиваем файл на несколько блоков (допустим как предлагали
> - мегабайтные блоки, или даже поменьше...)...
> Если проге захотелось каких-то данных

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


 
Almaz ©   (2004-06-22 04:55) [33]


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

Это не менеджер памяти - это Вы очень точно описали тот самый 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;


Удачи.


 
Digitman ©   (2004-06-22 08:25) [34]


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


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

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


 
Anatoly Podgoretsky ©   (2004-06-22 08:45) [35]

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


 
Digitman ©   (2004-06-22 08:57) [36]


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


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


 
Igorek ©   (2004-06-22 10:45) [37]


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

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


 
wicked ©   (2004-06-22 11:01) [38]

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


 
wicked ©   (2004-06-22 11:01) [39]

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:02) [40]

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



Страницы: 1 2 вся ветка

Текущий архив: 2004.07.11;
Скачать: CL | DM;

Наверх




Память: 0.6 MB
Время: 0.059 c
1-1088599378
Provodnick
2004-06-30 16:42
2004.07.11
PasteExcelTable не работает в Word


14-1087838328
MacroDenS
2004-06-21 21:18
2004.07.11
Матак Пати......


3-1087301447
max1000
2004-06-15 16:10
2004.07.11
При печати Word а через мою программу выдает ошибку


3-1087358807
Tornado
2004-06-16 08:06
2004.07.11
Как отменить редактирование?


14-1087392519
MBo
2004-06-16 17:28
2004.07.11
Sha - поздравляю !