Форум: "Основная";
Текущий архив: 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]упс... а куда ушло форматирование?... :(
Страницы: 1 2 вся ветка
Форум: "Основная";
Текущий архив: 2004.07.11;
Скачать: [xml.tar.bz2];
Память: 0.59 MB
Время: 0.035 c