реклама на сайте
подробности

 
 
> Необычное использование аппаратного умножителя
Rst7
сообщение May 7 2008, 17:14
Сообщение #1


Йа моск ;)
******

Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610



Вот, сижу, мастерю один проектик. Понадобилось сделать Хаффмана. В принципе, ничего сложного, однако работа с битовыми полями - это всегда узкое место процов с отсутствием комманд для обработки данных такого класса. Например, при упаковке по Хаффману необходима процедура, которая запишет в выходной поток n бит значения - вызывается примерно так:
Код
write_bits(huff->codes[val],huff->bits[val])

где первый параметр - код, а второй - собственно его размер в битах. Вот обычно эта функция - write_bits и несет основные затраты по времени выполнения. Почему так происходит - понятно, надо сдвигать входные биты в нужное положение, вычислять новое положение в буфере и т.д.

Посему, сразу кодить не стал, сел - подумал... biggrin.gif

И вот что надумал...

У нас есть аппаратный умножитель, который за 2 такта даст нам сдвинутый сразу в двух направлениях байт - и влево, и вправо. Конечно, в качестве множителя надо использовать маску - 0x01,0x02,...,0x80 и в результате, после выполнения комманды MUL в R0 будем иметь байт данных, сдвинутый влево, в R1 - сдвинутый вправо. Это уже отлично, одной коммандой мы готовим данные для OR с текущим байтом и для занесения следующих данных в накопитель. Однако, соответствующую текущему моменту маску еще надо получить. Она зависит как от текущей битовой позиции в выходном буфере, так и от размера битовых данных. Опять пришлось поразмышлять. Размышления натолкнули на идею хранить битовую позицию в буфере тоже в виде маски:
Код
0x80(биты 7...0 свободны),0x40(бит 7 с данными, биты 6...0 свободны),....,0x01(биты 7...1 с данными, бит 0 свободен)


Если размер символа в битах тоже задать в виде маски, то происходит чудо wink.gif - умножение (аппаратное, конечно) размера на битовую позицию даст новую позицию и она же является необходимым (точнее, в 2 раза меньше) множителем для сдвига символа. Причем, в зависимости, от того, какой байт результата, старший или младший, не равен 0 - то это и есть выбор двух альтернатив - новый символ требует перехода через границу байта или не требует.
Осталась маленькая тонкость, что новая позиция как множитель данных в 2 раза меньше (т.е. сдвинута вправо). Можно конечно сдвинуть при помощи LSL, но лучше использовать тот факт, что FMUL - есть Rs*Rd<<1, это поможет в оптимизации.

Букаф много (могут ниасилить), посему код:

Код
//Кластеризация вручную, при необходимости изготавливается нормальная структура
struct
{
  char bitstream_byte;
  char bitstream_bit;
  char *bitstream;
};

//Инициализация битового потока
//p - указатель на первый байт выходного буфера
void init_bitstream(char *p)
{
  bitstream_byte=0;
  bitstream_bit=0x80;
  bitstream=p;
}

//Финализация потока - слив неслитого
//bp - указатель на первый байт выходного буфера (для расчета размера)
//на выходе - размер потока в байтах
unsigned int finish_bitstream(char *bp)
{
  char *p=bitstream;
  if (bitstream_bit!=0x80) *p++=bitstream_byte;
  return p-bp;
}

void write_bits_by_mask(char sym, char msb);

//Запись n бит из символа sym, необходимо, чтобы неиспользуемые биты равнялись 0
void write_n_bits(char sym, char n)
{
  //Генерация обратной маски
  static __flash const char mask[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
  write_bits_by_mask(sym,mask[n-1]);
}

//Записать в битовый поток биты из sym
//Биты записываются от 0 до бита, определенного установленным битом в msb в обратном порядке
//msb=0x80 sym=0000000a
//msb=0x40 sym=000000ab
//msb=0x20 sym=00000abc
//msb=0x10 sym=0000abcd
//msb=0x08 sym=000abcde
//msb=0x04 sym=00abcdef
//msb=0x02 sym=0abcdefg
//msb=0x01 sym=abcdefgh
//Запись идет от 7 к 0, например запись символов 000abcde, 0000xyzt будет лежать в байтах как
//[abcdexyz][t.......]
//Если есть возможность хранить msb, лучше применять эту функцию, меньше накладных расходов
void write_bits_by_mask(char sym, char msb)
{
  char b=bitstream_byte;
  char m=bitstream_bit;
  char *p=bitstream;
  //--> Если есть возможнось, эта часть инлайнится во внешний цикл
  unsigned short i;
  i=__multiply_unsigned(m,msb); //ih - множитель для sym, чтобы согласовать положение
  if (i>>8)
  {
    //Требуется сдвиг влево
    m=i>>8;
    i=__fractional_multiply_unsigned(sym,m);
    b|=i;
  }
  else
  {
    //Требуется сдвиг вправо
    m=i;
    i=__fractional_multiply_unsigned(sym,m);
    b|=i>>8;
    *p++=b;
    b=i;
  }
  //<-- Конец части для инлайна
  bitstream=p;
  bitstream_byte=b;
  bitstream_bit=m;
}


Компилятор - IAR 5.10

Ну и листинг, который особо радует глаз (приведу только внутренности функции write_bits_by_mask)
Код
     54          void write_bits_by_mask(char sym, char msb)
   \                     write_bits_by_mask:
     55          {
   \   00000000   2F5B               MOV     R21, R27
   \   00000002   2F6A               MOV     R22, R26
   \   00000004   2F20               MOV     R18, R16
     56            char b=bitstream_byte;
   \   00000006   ....               LDI     R30, LOW(_A_bitstream_byte)
   \   00000008   ....               LDI     R31, (_A_bitstream_byte) >> 8
   \   0000000A   8130               LD      R19, Z
     57            char m=bitstream_bit;
     58            char *p=bitstream;
   \   0000000C   81A2               LDD     R26, Z+2
   \   0000000E   81B3               LDD     R27, Z+3
   \   00000010   8101               LDD     R16, Z+1
     59            //--> Если есть возможнось, эта часть инлайнится во внешний цикл
     60            unsigned short i;
     61            i=__multiply_unsigned(m,msb); //ih - множитель для sym, чтобы согласовать положение
   \   00000012   9F01               MUL     R16, R17
     62            if (i>>8)
   \   00000014   2011               TST     R1
   \   00000016   F021               BREQ    ??write_bits_by_mask_0
     63            {
     64              //Требуется сдвиг влево
     65              m=i>>8;
   \   00000018   2D41               MOV     R20, R1
     66              i=__fractional_multiply_unsigned(sym,m);
     67              b|=i;
   \   0000001A   032C               FMUL    R18, R20
   \   0000001C   2930               OR      R19, R0
   \   0000001E   C005               RJMP    ??write_bits_by_mask_1
     68            }
     69            else
     70            {
     71              //Требуется сдвиг вправо
     72              m=i;
   \                     ??write_bits_by_mask_0:
   \   00000020   2D40               MOV     R20, R0
     73              i=__fractional_multiply_unsigned(sym,m);
   \   00000022   032C               FMUL    R18, R20
     74              b|=i>>8;
     75              *p++=b;
   \   00000024   2931               OR      R19, R1
   \   00000026   933D               ST      X+, R19
     76              b=i;
   \   00000028   2D30               MOV     R19, R0
     77            }
     78            //<-- Конец части для инлайна
     79            bitstream=p;
   \                     ??write_bits_by_mask_1:
   \   0000002A   83A2               STD     Z+2, R26
   \   0000002C   83B3               STD     Z+3, R27
     80            bitstream_byte=b;
   \   0000002E   8330               ST      Z, R19
     81            bitstream_bit=m;
   \   00000030   8341               STD     Z+1, R20
     82          }
   \   00000032   2FA6               MOV     R26, R22
   \   00000034   2FB5               MOV     R27, R21
   \   00000036   9508               RET


Конечно, если есть возможность, выжимку кода необходимо вставлять прямо в цикл генерации символов, чтобы меньше оверхеда было на загрузку/выгрузку. В этом случае добавление битового поля будет занимать 10 или 12 тактов, в зависимости от того, происходит переход через границу байта или не происходит.

Опять же, если есть возможность, надо хранить размеры символов в виде масок, а не в обычном виде - это уберет оверхед на функцию write_n_bits (7 тактов).

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

Вообще, подобный подход можно использовать там, где необходимо организовывать сдвиги на произвольное количество бит.


--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
Go to the top of the page
 
+Quote Post

Сообщений в этой теме
- Rst7   Необычное использование аппаратного умножителя   May 7 2008, 17:14
- - SasaVitebsk   Цитата(Rst7 @ May 7 2008, 21:14) Вообще, ...   May 7 2008, 18:22
- - Rst7   ЦитатаПравда если есть возможность организовывать ...   May 7 2008, 18:50
|- - galjoen   А вот мне только-что мысль пришла - ещё не проверя...   May 7 2008, 19:49
|- - Rst7   Цитата(galjoen @ May 7 2008, 22:49) А вот...   May 8 2008, 07:24
- - vet   galjoen 2*255 = 510 = 0x01FE, 7 бит=1 3*255 = 765 ...   May 8 2008, 04:22
- - vet   сдвигами четность все равно шустрее выйдет: Код M...   May 8 2008, 07:54
|- - Rst7   Цитата(vet @ May 8 2008, 09:54) сдвигами ...   Dec 13 2008, 21:05
|- - singlskv   Цитата(Rst7 @ Dec 14 2008, 00:05) Случайн...   Dec 14 2008, 19:11
|- - Rst7   Цитата(singlskv @ Dec 14 2008, 21:11) ИМХ...   Dec 14 2008, 19:57
|- - singlskv   Цитата(Rst7 @ Dec 14 2008, 22:57) Да вооб...   Dec 14 2008, 20:08
|- - Rst7   Цитата(singlskv @ Dec 14 2008, 22:08) я в...   Dec 14 2008, 21:38
- - Rst7   Цитатасдвигами четность все равно шустрее выйдет: ...   May 8 2008, 08:00
- - _Pasha   Америки не открою, и особой экзотики в использован...   May 29 2008, 19:41
- - ae_   Как быть, когда результат (zL:zH)/yH > 255? про...   May 31 2008, 11:55
|- - _Pasha   Цитата(ae_ @ May 31 2008, 14:55) Как быть...   May 31 2008, 18:57
- - Rst7   Ну, продолжим наши игры. На этот раз под руки попа...   Jan 23 2009, 15:21
|- - galjoen   Цитата(Rst7 @ Jan 23 2009, 18:21) Ну, про...   Jan 27 2009, 10:38
- - Rst7   ЦитатаЯ собственно собираюсь переписать всё на асм...   Jan 27 2009, 11:22
|- - galjoen   Цитата(Rst7 @ Jan 27 2009, 14:22) Гм. Я р...   Jan 27 2009, 12:11
- - Rst7   ЦитатаПриведём (преобразуем) ваш вариант к такому ...   Jan 27 2009, 12:51
|- - galjoen   Цитата(Rst7 @ Jan 27 2009, 15:51) Почему?...   Jan 27 2009, 14:41
- - Rst7   ЦитатаНе совпадает с тем, что в описании. Где не ...   Jan 27 2009, 14:58
|- - galjoen   Цитата(Rst7 @ Jan 27 2009, 17:58) Ну вот...   Jan 28 2009, 05:25
- - Rst7   ЦитатаПоэтому м.б. не ; выполнил все Си-шные согла...   Jan 28 2009, 06:57
- - galjoen   Цитата(Rst7 @ Jan 28 2009, 09:57) Но на с...   Jan 28 2009, 15:53
- - galjoen   Сделал 50-ти тактовую версию беззнакового деления ...   Jan 30 2009, 10:06


Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 24th June 2025 - 08:45
Рейтинг@Mail.ru


Страница сгенерированна за 0.01509 секунд с 7
ELECTRONIX ©2004-2016