Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Управление данными в TMemoryStream


Автор: MuadDib 24.9.2007, 07:31
Здравствуйте, уважаемые. 
Возникла потребность работать с большими объемами данных помещенных в TMemotyStream объект. К примеру с большими файлами, загруженными в поток памяти. Необходимо удалять из памяти определенным образом помеченные блоки и соответствующим образом корректировать размер потока памяти.
Для работы с потоком памяти помимо его стандартных средств исползую указатель на его данные: 

код условно-описательный, чтобы показать, что делаю:

Код

TMemoryStream *MStream = new TMemoryStream;
char *MainBuf = (char*)MStream->Memory; //указывает на данные в потоке памяти

First(); //на начало
while(!Eof()) //пока не конец блока
{
if(CheckDelRecord()) //проверка на признак необходимости удаления записи
{
MoveMemory((MainBuf + Offset1),(MainBuf + Offset2),MStream->Size - Offset2); //копируем данные со смещения Offset2 на 
//Offset1 размером от Offset2 до конца файл
//т.е. "подтягиваем" на место удаленного блока все остльные данные 
MStream->SetSize(Offset1 - Offset2);//и уменьшаем размер потока памяти на количество "удаленных" байт
}
else
Next();
}


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

Еще вариант, тоже условно-описательный:

Код

First();
int R = 0,
N = 0,
K = 0;

while(K != RecordCount) //RecordCount - количество записей т.е. блоков данных определенного размера в потоке памяти
{
K = N;
FCurrentRecord = N; //указывает на текущую запись

if(CheckDelRecord()) //если запись нужно удалить
{
if(K == RecordCount)
break;

FCurrentRecord = firstNDRec;
while((K++ != RecordCount) && (CheckDelRecord())) //ищем следующую запись которую удалять не нужно
FCurrentRecord = firstNDRec = K ;

if(!CheckDelRecord()) //если нашли
{
FCurrentRecord = R = N; //для рассчета смещений Offset1 и Offset2 описание расчета не привожу для краткости 
MoveMemory((MainBuf + Offset1),(MainBuf + Offset2),RecordSize); //копируем данные из "неудаленной" записи в ту область которую "удалить" нужно
FCurrentRecord = K;
DeleteRecord(); //помечаем скопированную запись чтобы удалить ее когда до нее доберемся дабы не делать дубли 
}
}
else
R = N;
}

if(R == 0)
Empty();
else
MStream->SetSize(MStream->Size - RecordSize * (RecordCount - R)); //ну и в конце уменьшаем размер потока до необходимого


Данных код тоже работает нормально с файлами не слишком большого размера. С большими файлами медленно, хотя и быстрее чем предыдущий.
Подскажите пожалуйста, как еще можно реализовать данную задачу с наибольшим быстродействием. Интересует именно быстродействие, затраченные ресурсы неважны. 
Заранее спасибо!

Автор: Mihhail 24.9.2007, 09:17
Цитата(MuadDib @  24.9.2007,  11:31 Найти цитируемый пост)
//RecordCount - количество записей т.е. блоков данных определенного размера в потоке памяти

Если все записи одинакового размера, то почему бы не перемещать на место удалённой записи самую последнюю запись из "хвоста".

И зачем постоянно менять размер выделенной памяти если:
Цитата(MuadDib @  24.9.2007,  11:31 Найти цитируемый пост)
 Интересует именно быстродействие, затраченные ресурсы неважны.

При необходимости можно только увеличивать выделеный блок, при чём с запасом. Ведь известно кол-во записей и выход на удалённые ("мусорные") участки исключён. Если идёт постоянное Del/Add записей это так же увеличит скорость.

Автор: MuadDib 24.9.2007, 09:34
Если все записи одинакового размера, то почему бы не перемещать на место удалённой записи самую последнюю запись из "хвоста".

Добавлено через 4 минуты и 58 секунд
Дело в том, что данные в потоке должны следовать в том порядке, в котором они были туда загружены, менять последовательность нельзя. Т.е. на место удаленного блока должен вставать следующий непомеченный к удалению блок. Размер выделенной памяти действительно можно изменять не каждый раз, а только единожды после всех операций по перемещению данных, как это сделано во втором примере, однако это не дает ощутимого результата.

Автор: Mihhail 24.9.2007, 11:09
Цитата(MuadDib @  24.9.2007,  13:34 Найти цитируемый пост)
 Размер выделенной памяти действительно можно изменять не каждый раз, а только единожды после всех операций по перемещению данных

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

А по алгоритму могу предложить следующее:
Цитата(MuadDib @  24.9.2007,  13:34 Найти цитируемый пост)
 на место удаленного блока должен вставать следующий непомеченный к удалению блок

  Время перемещения всех записей всё равно не уменьшить, но можно сделать что бы программа не зависала, т.е. не перемещать все записи за раз. А создавать отдельный поток или просто таймер, который через небольшие промежутки времени смещает блоки небольшой длины(поэксперементировать с их размером). Пока единственное что приходит в голову....

Добавлено через 3 мин
А если забить на это перемещение? Нам же известны номера удалённых записей, при просмотре просто перескакивать через них.

Автор: Alexeis 24.9.2007, 11:50
Цитата(MuadDib @  24.9.2007,  07:31 Найти цитируемый пост)
Данных код нормально работает, с файлами не слишком большого размера. С большими файлами - очень медленно.

  Перед тем как придумывать определите какие файлы являются большими и каких ресурсов не жалко. Все таки файлы бывают и 100Гб, а в ОЗУ выделить непрерывный блок в 1Гиг порой невозможно. 

Автор: Vyacheslav 24.9.2007, 12:09
А зачем в приницпе держать это все в  TMemotyStream  постоянно? А если уж это так нужно, то почему не использовать вектор объектов TMemotyStream?

Автор: MuadDib 24.9.2007, 12:14
Если вывести упаковку в отдельный поток на длительное время (т.к. файл размером, например 200 МБ будет паковаться достаточно долго) , получится, что работать с данными будет все равно нельзя, так как они будут постянно изменяться и не будут актуальны. Это получится как работа с реляционной БД в отсутствии транзакций - не знаешь есть ли физически запись в потоке с которой собираешься работать или нет ее и подобные проблемы. Забить тоже нельзя, т.к. смысл упаковки в том, чтобы в конечном итоге уменьшить размер конечного потока и сохранить его в файл. Т.е. ресурсозатраты неважны на этапе работы с потоком, но затем файл, сформированный в итоге должен занять возможный минимум дискового пространства.
Такая вот неприятная штука получается... Нужно и с елки съехать и, желательно, седалище не оцарапать

Автор: MuadDib 24.9.2007, 12:39
Если не затруднит, хотелось бы попросить Ермолаева Вячеслава привести пример корректного создания вектора объектов TMemoryStream и работы с ним. 
Дело в том, что по-моему вариант: vector <TMemoryStream *> не слишком подходит. Желательно, как мне кажется, хранить не указатели, а сами потоки. И т.к. я не имею большого опыта работы с векторами не соображу как сделать верно... Хотя идея такая тоже приходила.

Автор: Alexeis 24.9.2007, 12:40
Предлагаю такой алгоритм. Сначала расчитать где должна быть каждая из записей, затем выделить новый блок нужной длинны и за один проход скопировать каждый блок строго на свое место. скопировать 200Мб в памяти это довольно быстро, должно быть не более 1й секунды.

Добавлено через 1 минуту и 48 секунд
Цитата(MuadDib @  24.9.2007,  12:39 Найти цитируемый пост)
Дело в том, что по-моему вариант: vector <TMemoryStream *> не слишком подходит.

  TMemoryStream - это клас VCL, потому не может быть статическим. 

Автор: MuadDib 24.9.2007, 12:49
Alexeis, я заню, что TMemoryStream не может быть статическим. А предложенный алгоритм начинаю писать, по-моему дельно, нужно пробовать...


Автор: Vyacheslav 24.9.2007, 14:48
Цитата(MuadDib @  24.9.2007,  12:39 Найти цитируемый пост)
Дело в том, что по-моему вариант: vector <TMemoryStream *> не слишком подходит. Желательно, как мне кажется, хранить не указатели, а сами потоки. И т.к. я не имею большого опыта работы с векторами не соображу как сделать верно... 

Кстати как раз удобнее во всех случаях хранить указатели. Сэкономите на копировании.
Если информацию, удалять-не удалять можно выудить из самого содержимого, то весь Ваш код по сжатию  уложится в  строкe плюс небольшая модификация CheckDelRecord( пишу по памяти без среды, поэтому могут быть ошибки )

Код


vector<TMemoryStream> vec;
bool CheckDelRecord(TMemoryStream* stream) 
{
     bool isDeleted = false;
     //здесь код по проверке нужно ли удалять 
     ...
     isDeleted = ...
     if( (isDeleted ) 
     {
          delete stream;
      }
     return isDeleted;
    
}

vec.erase( remove_if(vec.begin(), vec.end(),  CheckDelRecord ), vec.end());


 

Автор: MuadDib 25.9.2007, 13:30
Всем спасибо, получилос сделать через вектор быстро и удобно. 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)