Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2004.07.11;
Скачать: [xml.tar.bz2];

Вниз

Как лучше работать с 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]

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


 
jack128 ©   (2004-06-22 11:32) [41]


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

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

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

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


 
Digitman ©   (2004-06-22 11:41) [42]


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


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


 
wicked ©   (2004-06-22 11:42) [43]


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

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


 
jack128 ©   (2004-06-22 11:52) [44]

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


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


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

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


 
jack128 ©   (2004-06-22 12:18) [46]


> 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 12:28) [47]


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

то есть?... если выполнилась ф-ция загрузки, значит он там... :)


> у мя такой вот тестик с 300 метровым файлом 1,5 минуты длился..

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


 
jack128 ©   (2004-06-22 12:36) [48]


> то есть?... если выполнилась ф-ция загрузки, значит он там...
> :)

Под ОЗУ я подрузамеваю модули памяти, а не память виндоуз. Память в Windows физически может распалогаться и на жестком диске и вообще на чем угодно, но эти насители имеют весьма низкую скорость чтения/записи в отличии от ОЗУ. Поэтому я и говорю, что мапирование файла скорость не может увеличить - этот аппаратная проблема. Мапирование лишь отображает файл на ВАП, физически файл никуда не копируется..


 
wicked ©   (2004-06-22 12:46) [49]

мапирование файла ускоряет доступ к нему не потому, что оно какое то волшебное, а потому, что оно фактически ничего не делает кроме указания менеджеру памяти, что под эти адреса этого процесса "подложен" этот файл...
загрузка данных происходит только тогда, когда аппликация обращается непосредственно к этим адресам... именно поэтому произвольный доступ к небольшим участкам (до 4 - 16 кб последовательно - 1 - 4 страницы памяти) происходит почти мгновенно - коммитятся и наполняются данными необходимые страницы...
если же после создания memory mapped файла попытаться последовательно прочесть в цикле все данные, то по скорости этот метод будет равен простому чтению файла с помощью ReadFile...


 
jack128 ©   (2004-06-22 12:56) [50]


>  именно поэтому произвольный доступ к небольшим участкам
> (до 4 - 16 кб последовательно - 1 - 4 страницы памяти) происходит
> почти мгновенно - коммитятся и наполняются данными необходимые
> страницы...

а что, если я эти небольшие участки памяти буду читать с помощью ReadFile будет медленнее? Но почему?


 
wicked ©   (2004-06-22 13:00) [51]


> а что, если я эти небольшие участки памяти буду читать с
> помощью ReadFile будет медленнее? Но почему?

не медленнее... геморойней - выделить память, seek, прочесть, опять seek, записать...
а так - отмерил offset скока надо и читай-пиши...


 
wicked ©   (2004-06-22 13:02) [52]

плюс не стоит забывать, что windows сама кеширует запись в мапленные участки памяти... и при закрытии нужно делать FlushViewOfFile...


 
jack128 ©   (2004-06-22 13:03) [53]

Итак вывод - MapFile - только для удобства!!! Скорость работы никак не измениться.


 
wicked ©   (2004-06-22 13:06) [54]

не совсем...
тут количество переходит в качество - удобство работы предполагает и наталкивает способы, которые никогда бы не пришли в голову, если бы работа с файлом велась через ReadFile...


 
jack128 ©   (2004-06-22 13:14) [55]

Да? Нужно попробовать... Хотя я так подумал, меня бы это натолкнуло на такие решения, которые бы замедлили работу с файламии :-)
Все таки подсознательно сидит - раз память, значит быстрый доступ..


 
wicked ©   (2004-06-22 13:18) [56]

тогда см [39] - писалось для себя...
хотя, если найдутся багофичи, то хотелось бы о них знать... ;)


 
MacroDenS ©   (2004-06-22 14:37) [57]


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


да при первом запуске конечно же неизвестно, а потом после первой загрузки становится известно, особенно если это учитывать.
К примеру можно же создать файл с такой структурой:

ID         LoadingNum
1                 159
4                  87
4567125            14
 
и так далее...


 
Алекс А   (2004-06-23 01:21) [58]


> Almaz ©   (22.06.04 04:55) [33]

Спасибо за пример! Попробовал так сделать.
Только у меня не PChar, а Base1 : array[0..SizeB1-1] of Longword;

Поменял на Base1 : PLongWord;, так на первом же вызове ( CountBit(Base1[i1] ) пишет ошибку [Error] Unit1.pas(615): Array type required. Что здесь не так ?


 
Роман   (2004-06-23 09:32) [59]

FileMapping отличается в корне от ReadFile: ответственность за кеширование в первом случае лежит на драйверах устройства (это может быть не только диск, но и, например, видеокарта AGP!) и осуществляется посредством прямого копирования данных в RAM посредством DMA, во втором же случае - используется буферизированная файловая система Windows. FileMapping всегда быстрее ReadFile т.к. специально разрабатывался для этих целей. Читайте Рихтера, господа.


 
Игорь Шевченко ©   (2004-06-23 10:30) [60]


> ответственность за кеширование в первом случае лежит на
> драйверах устройства


Ответственность за кеширование лежит на менеджере кэша, как ни странно


 
Игорь Шевченко ©   (2004-06-23 10:32) [61]


> FileMapping ...
> осуществляется посредством прямого копирования данных в
> RAM посредством DMA


Бред


 
Anatoly Podgoretsky ©   (2004-06-23 11:14) [62]

Роман   (23.06.04 09:32) [59]
Здесь правильно только про Рихтера.


 
Palladin ©   (2004-06-24 00:00) [63]

:)))


 
Almaz ©   (2004-06-24 00:43) [64]


> Алекс А   (23.06.04 01:21) [58]
>
> > Almaz ©   (22.06.04 04:55) [33]
>
> Спасибо за пример! Попробовал так сделать.
> Только у меня не PChar, а Base1 : array[0..SizeB1-1] of
> Longword;
>
> Поменял на Base1 : PLongWord;, так на первом же вызове (
> CountBit(Base1[i1] ) пишет ошибку [Error] Unit1.pas(615):
> Array type required. Что здесь не так ?

Дело в том, что PChar рассматривается Object Pascal как указатель на массив, поэтому запись MyArray[I] - правомерна. А PLongWord - это просто указатель на переменную типа LongWord. Поэтому, для использования массива LongWord его надо описать:

type
 TLongwordArray = array [0..MaxInt div 16 - 1] of Longword;
 PLongwordArray = ^TLongwordArray;
...
 MyArray: PLongwordArray;
...


А в вашем случае: PLongword = ^Longword; - это не массив, поэтому компилятор "ругается".

Удачи.


 
default ©   (2004-06-24 00:58) [65]

Anatoly Podgoretsky ©   (23.06.04 11:14) [62]
да, читал я про MMF у Рихтера ничего подобного там не было


 
Алекс А   (2004-06-24 01:57) [66]


> Almaz ©   (24.06.04 00:43) [64]


Заработало ! :) Грузит моментом. Торможения даже незаметно. Или может просто задачи подходящей не попалось ещё ? Но всё равно очень здорово. Спасибо !
Всем рекомендую.

А с насколько большими файлами можно так работать ? Можно ли с файлами большими High(LongWord) или хотя бы с большими High(Integer) ?



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

Форум: "Основная";
Текущий архив: 2004.07.11;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.67 MB
Время: 0.055 c
3-1087307503
denis24
2004-06-15 17:51
2004.07.11
Внешнее обьединение с несколькими таблицами


1-1088357174
Sphinx
2004-06-27 21:26
2004.07.11
Объеденение текста в RichEdit


1-1088216448
x_byte
2004-06-26 06:20
2004.07.11
webbrowser и его parent


1-1087978510
Zema
2004-06-23 12:15
2004.07.11
Вопрос про Grid и ButtonStyle=cbsEllipsis...


14-1088082581
RealRascal
2004-06-24 17:09
2004.07.11
Fuzz





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский