#delphi #оптимизация #хеширование #словари #биоинформатика
Учитывая, что теперь можно спокойно выделять большие объемы памяти внутри TMemoryStream,
я вернулся к идее хранения данных геномных исследований, используемых нами внутри TDictionary
в файле для будущего повторного использования. Класс определен так:
type
PosIndex = packed record
chr, pos:integer;
end;
PosIndexData = record
gname, rname, promoter: string;
count:array[0..NOfTissues-1] of byte;
end;
TPosDict = class (TDictionary)
private
procedure SaveToStream(stream: TStream);
procedure LoadFromStream(stream: TStream);
public
procedure SaveToFile(filename:string);
procedure LoadFromFile(filename:string);
procedure LoadFromZip(AFileName, InnerName: string);
procedure SaveToZip(AFileName, InnerName: string);
end;
Предупреждая вопросы и комментарии в стиле "Зачем нужен TDictionary, когда есть базы
данных?", сразу скажу: у нас мобильное (не в плане телефона, а в плане, что оно часто
запускается где попало) приложение, мы не можем использовать стационарный сервер БД,
как коллега в своём вопросе Как оптимизировать таблицы/запрос в MySQL?, а работа с
файловыми БД с нашими объёмами данных, увы, крайне медленна. А вот с TDictionary поиск
происходит пусть не мгновенно, но для нас вполне подходяще по времени.
Запись в поток (этот метод затем используют и SaveToFile и SaveToZip) происходит так:
procedure TPosDict.SaveToStream(stream: TStream);
var
writer: TWriter;
ps:PosIndex;
pid:PosIndexData;
l:integer;
begin
writer := TWriter.Create(stream, 4096);
l:=sizeof(pid.count);
try
writer.WriteListBegin;
for ps in Self.Keys do
begin
pid:=Items[ps];
writer.WriteInteger(ps.chr);
writer.WriteInteger(ps.pos);
writer.WriteString(pid.gname);
writer.WriteString(pid.rname);
writer.WriteString(pid.promoter);
writer.Write(pid.count,l);
end;
writer.WriteListEnd;
finally
writer.Free;
end;
end;
Метод быстр, гигабайтные данные сохраняются быстро даже в ZIP-файл. А вот считывание
из файла крайне медленно из-за того, что данные добавляются во вновь созданный TDictionary,
происходит хэширование и проверка на уникальность:
procedure TPosDict.LoadFromStream(stream: TStream);
var
reader: TReader;
ps:PosIndex;
pid:PosIndexData;
l:integer;
begin
Clear;
l:=sizeof(pid.count);
reader := TReader.Create(stream, 9192);
try
reader.ReadListBegin;
while not reader.EndOfList do
begin
ps.chr:=reader.ReadInteger;
ps.pos:=reader.ReadInteger;
pid.gname:=reader.ReadString;
pid.rname:=reader.ReadString;
pid.promoter:=reader.ReadString;
reader.Read(pid.count,l);
Add(ps,pid); // вот это всё тормозит!!!
end;
reader.ReadListEnd;
finally
reader.Free;
end;
end;
Избежать этого, как я понимаю, нельзя. Но ведь это уже было сделано, когда объект
существовал ранее, все хэши уже были созданы и работали. Появилась идея: можно ли при
сохранении TDictionary как-то сохранить объект целиком, включая хэши, а затем так же
восстановить из файла, чтобы не тратилось время на перехэширование. Ну, или другие
идеи, как убрать бутылочное горло при восстановлении данных.
Ответы
Ответ 1
Особенность TDictionary в том, что когда заканчивается ёмкость под хэши, он увеличивает размер и в этот момент происходит перехэширование всей имеющийся (на данный момент) коллекции. Поэтому если заранее примерно известен размер коллекции, то этот размер умноженный на 2-3 можно поставить в capacity.
Комментариев нет:
Отправить комментарий