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

Вниз

Подвисание при работе с большим динамическим массивом байтов   Найти похожие ветки 

 
Alik   (2010-10-15 00:17) [0]

Добрый день,

Имеется массив -

RawData: array of Byte;
DataLen := 4708000;
SetLength(RawData, DataLen)

Размер массива составляет порядка 4.5 Мегабайт.
Необходимо максимально быстро вырезать определенные куски в данном массиве, так как это делается например в функции Delete(S, Index, Count);

Пробовал так
for i := Index to DataLen - Count - 1 do
   RawData[i] := RawData[i+Count];

Это работает, но тормозит очень сильно (порядка 30 секунд).

Можно ли как то ускорить эту процедуру средствами API или через Assembler?


 
Rouse_ ©   (2010-10-15 01:10) [1]

Можно, не обязательно грохать сами данные нарываясь на реаллоки, достаточно работать с записями через промежуточный список с указателями на записи из которого исключать не валидные записи. Ну а сам реаллок делать после полной процедуры удаления.


 
Германн ©   (2010-10-15 03:29) [2]


> Rouse_ ©   (15.10.10 01:10) [1]

В приведенном обрывке кода вроде никаких реаллоков нет. Возможно они подразумеваются, но ...


 
Anatoly Podgoretsky ©   (2010-10-15 04:16) [3]

Не надо делать это побайтно.


 
RWolf ©   (2010-10-15 09:43) [4]


> Это работает, но тормозит очень сильно (порядка 30 секунд).

Бред. Вышеприведённый код выполняется мгновенно.


 
DVM ©   (2010-10-15 10:47) [5]


> Пробовал так
> for i := Index to DataLen - Count - 1 do
>    RawData[i] := RawData[i+Count];

Move не спасет отца русской демократии?


 
han_malign   (2010-10-15 10:53) [6]


> Вышеприведённый код выполняется мгновенно.

- если {$R-}
да и move() все же побыстрее будет...

А вот если надо вырезать много(N) маленьких кусков - то при последовательных удалениях время выполнения O(N*DataLen), тогда как при сквозной обработке*(см. [1]) - O(DataLen)...


 
RWolf ©   (2010-10-15 13:13) [7]


> han_malign   (15.10.10 10:53) [6]
> если {$R-}

да даже если R+, всё равно мгновенно.
включив вообще всю отладку, не выбрался даже за 50 мсек (на атлоне 3000); типичное время 16-32 мсек.


 
Alik   (2010-10-15 16:10) [8]


> RWolf ©   (15.10.10 13:13) [7]
>
>
> > han_malign   (15.10.10 10:53) [6]
> > если {$R-}
>
> да даже если R+, всё равно мгновенно.
> включив вообще всю отладку, не выбрался даже за 50 мсек
> (на атлоне 3000); типичное время 16-32 мсек.
>


Я забыл упомянуть, что такая операция (вырезание части динамического массива) может выполняться несколько сот раз. Это используется для фильтрации нулей из первоначального массива, нули могут быть в любом месте и в любом кол-ве. Поэтому процедура и занимает столько времени!


 
Alik   (2010-10-15 16:12) [9]


> DVM ©   (15.10.10 10:47) [5]
>
>
> > Пробовал так
> > for i := Index to DataLen - Count - 1 do
> >    RawData[i] := RawData[i+Count];
>
> Move не спасет отца русской демократии?


А как использовать Move для нашего массива (RawData: array of Byte)?
Ведь эта функция используется для переменной типа String!


 
Sha ©   (2010-10-15 16:13) [10]

> Alik   (15.10.10 16:10) [8]

Сделай циклом за один проход массива.


 
Sha ©   (2010-10-15 16:15) [11]

И двигай только куски, в которых нет нулей


 
Alik   (2010-10-15 16:37) [12]


> Sha ©   (15.10.10 16:15) [11]
>
> И двигай только куски, в которых нет нулей


Все и так делается за один проход. Вот текст кода:

var
 RawData: array of Byte;
 ByteId, i, j: Integer;
 DataLen: Integer;
 CountZeros: Integer;
 S: String;
begin
   DataLen := 4608000;
   SetLength(RawData, DataLen);

   Далеее  RawData наполняется значениями
  и приступаем к выкидыванию нулей по такому алгоритму: Если кол-во последовательных нулей CountZeros меньше 4, то пропускаем, если CountZeros > 3, то вместо первого нуля пишем 255, а потом в следующих двух нулях пишем два байта (кол-во последовательных нулей), затем все следующие последовательные нули вырезаем и сокращаем длину массива на CountZeros - 3 элементов.

  ByteId := -1;
  repeat
     Inc(ByteID);
     CountZeros := 0;
     while (RawData[ByteID] = 0) and (ByteID < DataLen) and (CountZeros < 65536) do begin
                     Inc(ByteID);
                     Inc(CountZeros);
                     end;
               if CountZeros > 3 then begin
                     S := WordTo2BytesStr(CountZeros);
                     Dec(ByteID, CountZeros - 3);
                     RawData[ByteID - 3] := 255;
                     RawData[ByteID - 2] := Byte(S[1]);
                     RawData[ByteID - 1] := Byte(S[2]);
                     j := CountZeros - 3;
                     for i := ByteID to DataLen - j - 1 do
                         RawData[i] := RawData[i + j];
                     Dec(DataLen, j);
                     SetLength(RawData, DataLen);
                     end;
             until ByteID >= DataLen;
            end;

// преобразовывает целое число в строку из двух символов
function WordTo2BytesStr(Val: Word): String;
var
 Temp: Word;
 TempArr: array[0..1] of Byte absolute Temp;
begin
 Temp := Val;
 Result := chr(TempArr[1]) + chr(TempArr[0]);
end;


 
Sha ©   (2010-10-15 16:45) [13]

Перепиши это без WordTo2BytesStr, string и SetLength внутри цикла.


 
RWolf ©   (2010-10-15 16:48) [14]


> Alik   (15.10.10 16:37) [12]
> for i := ByteID to DataLen - j - 1 do

то есть двигаешь массив с текущего индекса до конца.
а зачем?
почему бы не двигать только до следующей цепочки нулей?


 
Alik   (2010-10-15 16:57) [15]


> Sha ©   (15.10.10 16:45) [13]
>
> Перепиши это без WordTo2BytesStr, string и SetLength внутри
> цикла.


Без WordTo2BytesStr, string пробовал, они едят ничтожно малое время, так как это вычисляется только один раз для найденой последовательной цепочки нулей, вто время как дальнейшее двигание массива влево производит очень много операций = кол-во оставшихся справа элементов массива.

Без SetLength тоже пробовал, ничего не изменилось.


 
Alik   (2010-10-15 17:00) [16]


> RWolf ©   (15.10.10 16:48) [14]
>
>
> > Alik   (15.10.10 16:37) [12]
> > for i := ByteID to DataLen - j - 1 do
>
> то есть двигаешь массив с текущего индекса до конца.
> а зачем?
> почему бы не двигать только до следующей цепочки нулей?


Вот пример исходного массива:
1 2 3 4 5 6 7 8 9 0 0 0 0 0 8 7 6 5 4 3 2 1 0 0 0 0 2 3 4 5 6 7 8 9

Вот пример выходного массива:
1 2 3 4 5 6 7 8 9 255 0 5 8 7 6 5 4 3 2 1 255 0 4 2 3 4 5 6 7 8 9


 
RWolf ©   (2010-10-15 17:03) [17]

об чем я и говорю.
после первой итерации сдвигается блок 8 7 6 5 4 3 2 1 0 0 0 0 2 3 4 5 6 7 8 9
а можно было бы ограничиться сдвиганием 8 7 6 5 4 3 2 1


 
Sapersky   (2010-10-15 17:35) [18]

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

Кстати: то, что ты делаешь, называется RLE-упаковка. Если это не учебное задание, имеет смысл поискать готовую реализацию. Или использовать ZLib, который сожмёт лучше (как архиватор Zip), и, вполне возможно, быстрее кривой самопальной реализации RLE.


 
RWolf ©   (2010-10-15 17:47) [19]


> Sapersky   (15.10.10 17:35) [18]

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


 
Sapersky   (2010-10-15 18:16) [20]

Да, точно, если с поправкой [17] - то одинаково.


 
Sha ©   (2010-10-15 18:25) [21]

> Alik   (15.10.10 16:57) [15]
> пробовал, ничего не изменилось.

Плохо пробовал, пробуй еще раз [10]+[11]+[13] пока все не изменится.
А когда изменится, начни сдвигать по 4 байта (dword) за раз, оно снова изменится.


 
Sha ©   (2010-10-16 22:27) [22]

function RLE(p: pByteArray; len: integer): integer;
var
 save, tmp: byte;
 read, write, count: integer;
begin;
 if len<=3 then Result:=len
 else begin;
   dec(len);                                  //индекс последнего байта
   save:=p[len];                              //сохраняем последний байт
   p[len]:=0; if p[len-1]=0 then p[len]:=1;   //меняем его так, чтобы на нем требовалось переключение
   read:=0;                                   //прочитано байтов
   write:=0;                                  //записано байтов
   repeat;
     dec(read);
     dec(write);
     repeat;                                  //копируем не нули
       inc(read);
       inc(write);
       tmp:=p[read];
       p[write]:=tmp;
       until tmp=0;                           //в write - индекс первого записанного нуля
     if read=len then begin;                  //если нуль был последним байтом
       p[write]:=save;                        //пишем вместо него сохраненное значение
       inc(write);
       break;
       end;
     count:=-read;                            //отсюда считаем количество нулей
     repeat;
       inc(read);
       until p[read]<>0;
     if read=len then inc(read);              //если мы здесь, то последний байт в массиве обязан быть нулем
     count:=count+read;                       //было такое количество нулей подряд
     if count<=3 then begin;
       write:=write+count;
       p[write-1]:=0;                         //копируем последний нуль, может быть поверх первого
       if count=3 then p[write-2]:=0;         //и средний, если надо
       end
     else while true do begin;
       if count>65535 then begin;             //64k нулей или более
         p[write]:=255;
         p[write+1]:=255;                     //сжимаем только 64k-4 нулей
         p[write+2]:=252;
         write:=write+3;
         count:=count-65532;                  //оставляем несжатыми 4 и более нулей
         end
       else begin;
         p[write]:=255;
         p[write+1]:=count shr 8;             //иначе сжимаем все
         p[write+2]:=count;
         write:=write+3;
         break;
         end
       end;
     until read>=len;
   Result:=write;
   end;
 end;

procedure TForm1.Button1Click(Sender: TObject);
const
 a: array[0..37] of byte= (1,2,3,4,5,6,7,8,9,0,0,0,0,0,8,7,6,5,4,3,2,1,0,0,0,0,2,3,4,0,5,0,6,7,8,9,0,0);
                         //1 2 3 4 5 6 7 8 9 255 0 5 8 7 6 5 4 3 2 1 255 0 4 2 3 4 0 5 0 6 7 8 9 0 0
var
 Data: array of byte;
 i: integer;
 s: string;
begin;
 SetLength(Data, High(a)+1);
 for i:=0 to High(a) do Data[i]:=a[i];
 i:=RLE(@Data[0], Length(Data));
 SetLength(Data, i);
 for i:=0 to Length(Data)-1 do s:=s + IntToStr(Data[i]) + " ";
 Edit1.Text:=s;
 end;


Особо не отлаживал.
Если нужна бОльшая скорость, первый вложенный repeat можно переписать, используя работу с двойными словами. Скорость возрастет еще почти в 4 раза, но при этом иногда группы из 4-6 нулей могут оставаться несжатыми.


 
Sha ©   (2010-10-16 22:58) [23]

> Alik   (15.10.10 00:17)  
> Можно ли как то ускорить эту процедуру средствами API или через Assembler?

Ускорения через Pascal [22] вроде достаточно :-)
Твои 4708000 байт обрабатываются менее одного тика системного таймера (менее 16 msec)


 
Alik   (2010-10-20 14:06) [24]


> Sha ©   (16.10.10 22:27) [22]
> function RLE(p: pByteArray; len: integer): integer;


Спасибо большое за код, а можно еще распаковщик получить?


 
Sha ©   (2010-10-20 15:14) [25]

Распаковщик тут http://guildalfa.ru/alsha/node/11



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

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

Наверх





Память: 0.53 MB
Время: 0.004 c
2-1287575888
Evgeniy Efimchenko
2010-10-20 15:58
2011.01.16
Работа с типизизованными файлами


2-1287812607
Илья2
2010-10-23 09:43
2011.01.16
Вызов не kernel32.dll функций из DllEntryPoint


3-1251200767
DelphiN!
2009-08-25 15:46
2011.01.16
Аналог Copy(как в Делфи) для TSQL


2-1287918930
Анна
2010-10-24 15:15
2011.01.16
многопоточная организация в Делфи


15-1285734450
И. Павел
2010-09-29 08:27
2011.01.16
Можно ли использовать невизуальные компоненты в др. потоке?





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский