Сейчас на форуме: rmn, Magister Yoda, vasilevradislav, tyns777, zombi-vadim (+4 невидимых)

 eXeL@B —› Программирование —› Помогите оптимизировать манипуляцию с битами на Си
Посл.ответ Сообщение


Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 27 апреля 2008 00:14
· Личное сообщение · #1

Изучаю Си в (CodeVisionAVR). Сделал вот такое нужное мне действие:

Установить бит с номером 'n' в переменной 'y' как бит с номером 'd' в переменной 'x'.
if ((x>>d)&1) {y|=(1<<n);} else {y&=~(1<<n);}


Хоть и работает, подозреваю, что сделал это криво. Наверняка можно оптимизировать. Прошу помощи.




Ранг: 1053.6 (!!!!), 1078thx
Активность: 1.060.81
Статус: Участник

Создано: 27 апреля 2008 01:38
· Личное сообщение · #2

a = ((x >> d) << n);
if (a != 0)
y |= a;
else
y &= ~a;

помоему тоже самое)




Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 27 апреля 2008 01:49 · Поправил: ToBad
· Личное сообщение · #3

reversecode пишет:
a = ((x >> d) << n);
if (a != 0)
y |= a;
else
y &= ~a;

помоему тоже самое)


Да, только лишняя переменная... Хотя нужно посмотреть сколько будет кода после компиляции в том и в другом случае.




Ранг: 387.4 (мудрец)
Активность: 0.170
Статус: Участник
системщик

Создано: 27 апреля 2008 06:08
· Личное сообщение · #4

ToBad, я думаю что там будет всего несколько инструкций, и далее оптимизировать нету смысла... в смысле если у тебя что-то медлено, то, думаю, проблема не тут. Кста, есть пара спец инструкций для таких дел: "bt r/m32, bit" и btr, btc




Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 27 апреля 2008 12:08
· Личное сообщение · #5

s0larian пишет:
ToBad, я думаю что там будет всего несколько инструкций, и далее оптимизировать нету смысла... в смысле если у тебя что-то медлено, то, думаю, проблема не тут. Кста, есть пара спец инструкций для таких дел: "bt r/m32, bit" и btr, btc


Дело в том, что пишу я как упомянул ранее под CodeVisionAVR. Это Си для микроконтроллеров. Там свой ассемблер, свои инструкции. Оптимизация нужна не для скорости, а для сокращения числа команд. Есть там конечно свои инструкции для манипуляции битами (bld и bst), которые копируют указанный бит во флаг и восстанавливают назад. Написать функцию с ассемблерными командами у меня не получилось из за того, что упомянутые команды принимают в параметрах регистр и константу. А в функцию я передаю всё через переменные.



Ранг: 122.2 (ветеран)
Активность: 0.040
Статус: Участник

Создано: 27 апреля 2008 20:28
· Личное сообщение · #6

ToBad пишет:
...не получилось из за того, что упомянутые команды принимают в параметрах регистр и константу.


Помню еще во времена ZX Spectrum в играх использовалась одна интересная оптимизация для уменьшения числа команд. Оптимизация эта была связана с моджификацией кода процедуры. Может и тут получится, если константу в битовой команде модифицировать на лету.



Ранг: 45.7 (посетитель), 5thx
Активность: 0.020
Статус: Участник

Создано: 27 апреля 2008 21:00 · Поправил: Fallout
· Личное сообщение · #7

reversecode пишет:
a = ((x >> d) << n);
if (a != 0)
y |= a;
else
y &= ~a;


Этот код вообще не к чему... задача установить только тот бит что надо... а с данным примером будут установлены и другие биты которые человеку не нужны ... пример ( переменные скажем 8битные, номер бита с 0 как понимаю ):


x = 0b11001100;
y = 0b00110011;

d = 1; /* берем значение первого бита в x */
n = 2; /* установим значение первого бита в бит номер 2 */


Выполняем твой код:


a = ((x >> d) << n);


после выполнения 'a' будет 0b10011000 далее твоё условие в if будет true .... так как дейсвительно оно не равно нуля.... хотя и второй бит равен нулю!!!! соотвенно дальше мы установим биты из переменной 'a' в переменую y. То есть мало того что в условии ошибка... так ещё мы и установили лишние биты.... Что же нехватает в коде ? А нехватает маски что отсечет лишние биты.... Например так :


a = ((x >> d) << n); -> a = (((x >> d)&0x01) << n);




Ранг: 65.3 (постоянный), 10thx
Активность: 0.020
Статус: Участник

Создано: 27 апреля 2008 23:07
· Личное сообщение · #8

y=(y&~(1<<n))|(((x>>d)&1)<<n);
Тоже 7 битовых операции, но нету условия




Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 28 апреля 2008 00:09
· Личное сообщение · #9

egorovshura пишет:
Оптимизация эта была связана с моджификацией кода процедуры. Может и тут получится, если константу в битовой команде модифицировать на лету.


Трудно сказать, я не настолько знаю Си, да ещё вариант для микроконтроллеров, что бы провернуть такое. Подозреваю, что с асм вставками можно провернуть всё компактно, если знать как и преодолеть проблемы с регистрами и константами.

tomac пишет:
y=(y&~(1<<n))|(((x>>d)&1)<<n);
Тоже 7 битовых операции, но нету условия


Хороший примерчик. Без условия мне больше нравится. Жалко, что код не изменился ни на байт. Размер остался тот же как ни странно. У меня это всё записано в #define и что интересно компилятор мой вариант в зависимости от передаваемых параметров превращает в ассемблерный код от двух строчек, до десятка с несколькими подпрограммами !

Нужно будет спросить на каком нибудь форуме у программеров микроконтроллеров. Они должны знать особенности этого ассемблера и его совмещения с Си, и думаю какие нибудь фишки там есть. Вариант с логикой наверное уже более оптимизировать некуда ?...



Ранг: 45.7 (посетитель), 5thx
Активность: 0.020
Статус: Участник

Создано: 28 апреля 2008 09:17
· Личное сообщение · #10

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

/*
что интересно компилятор мой вариант в зависимости от передаваемых параметров превращает в ассемблерный код от двух строчек, до десятка с несколькими подпрограммами !
*/

Если касаемо именно этого варианта y=(y&~(1<<n))|(((x>>d)&1)<<n); то скорее всего ты в макрос передавал не 8битные переменные... а так как нативно микроконтроллер твой скорее всего (мега или тини небось ? ) 8битный то и переменные большего размера он уже нативно не может поддерживать поэтому может быть и сдвиги и прочие операции над такими переменными он делает через соответствующие подпрограммы.... всё от компилятора конечно зависит и от уровня оптимизации в настройках....

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




Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 28 апреля 2008 14:40
· Личное сообщение · #11

Fallout пишет:
скорее всего ты в макрос передавал не 8битные переменные... а так как нативно микроконтроллер твой (мега или тини небось ? )


Ну в конфиге проекта указывал именно что бы 8-ми битные были. Контроллер мега8515.

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

Я и сам себе задаю этот вопрос. В общем то успеваю, да и запас есть по кварцу. Впаял 8Mhz, а контроллер на 16 может работать. Вот и оптимизация скорости в два раза. Просто режут глаз конструкции:

...
COPYBIT(st2,statkey1,st2pin,PORT1);
COPYBIT(cn,statkey1,cnpin,PORT1);
COPYBIT(at,statkey1,atpin,PORT1);
COPYBIT(bt,statkey0,btpin,PORT2);
COPYBIT(sl2,statkey0,sl2pin,PORT2);
...


...и таких много.



Ранг: 45.7 (посетитель), 5thx
Активность: 0.020
Статус: Участник

Создано: 28 апреля 2008 18:22
· Личное сообщение · #12

ToBad пишет:
Ну в конфиге проекта указывал именно что бы 8-ми битные были.


Ну char то понятно, но мало ли ты там short , int используешь .... вот тебе компилятор и генерит к ним "обвес"
....




Ранг: 387.4 (мудрец)
Активность: 0.170
Статус: Участник
системщик

Создано: 28 апреля 2008 19:25
· Личное сообщение · #13

ToBad, стой секунду, ты биты читаешь из memory-mapped регистра какого-то устройства? Если да, то ты бы спросил про "красивый С код" а не оптимизацию. Это делается проще через struct и bit fields:


struct Uart
{
byte rx_fifo;
byte flag1:1, flag2:1, flag3:1, reserved:5;
};


потом просто объявляешь глобальный volatile указатель этого типа, и пользуешься полями - компилер выплюнет код для выделения конкретного бита. Тебе только надо удостоверится что читаются байты, а не machine word, то есть компилер знает что эти адреса лежат в области с byte достопом (это не RAM).



Ранг: 45.7 (посетитель), 5thx
Активность: 0.020
Статус: Участник

Создано: 28 апреля 2008 21:50
· Личное сообщение · #14

s0larian пишет:
ты биты читаешь из memory-mapped регистра какого-то устройства


У него 8битный микроконтроллер ( риск процессор + рам + ром на борту ) фирмы Атмел... там грубо говоря РАМ и представляет из себя кучу регистров общего назначения... тока определенная область их являются регистрами специального назначения такие как регистры портов ввода вывода и тд... Битовые поля как вариант только не всегда компилятор генерит оптимальный код для таких дел Я бы сказал почти никогда... Но интересно To Bad скажешь потом скока получилось по коду ?

п.с: вообще заметил тема МК тут появляться уже не раз что радует




Ранг: 387.4 (мудрец)
Активность: 0.170
Статус: Участник
системщик

Создано: 28 апреля 2008 22:31
· Личное сообщение · #15

Fallout, ToBad, просто вопрос в том эти биты в твоих переменных или в регистрах железа? Если первое, то флаги на С реализовывают именно полями. Если второе, то эти поля можно присобачить и к железу, но надо проверять код.



Ранг: 45.7 (посетитель), 5thx
Активность: 0.020
Статус: Участник

Создано: 29 апреля 2008 09:09
· Личное сообщение · #16

Биты в регистрах.... Тут проблем не будет можно присвоить адрес регистра структуре... но это две доп. переменные... + как он к полям структуры будет обращаться если у него номер бита что надо читать в переменной.... была бы константа можно было бы макрос сделать.... а тут switch/if-else городить

п.с: компилятор поддерживает кстати бит ассес к ИО портам... ( см. док 3.13.1 )


/* Set bit 0 of PORTA output */
PORTA.0 = 1;


Так что вот как вариант... только мало поможет если опять таки номер пина не константа.... ещё поддерживается тип данных bit вот тут можно тоже поэксперементировать




Ранг: 1053.6 (!!!!), 1078thx
Активность: 1.060.81
Статус: Участник

Создано: 29 апреля 2008 11:19
· Личное сообщение · #17

проще уже смотреть как компилятор соптимизирует на asm
а то может какая то большая и неуклюжая манипуляция на С будет оптимальнее на asm
чем красивая и краткая




Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 29 апреля 2008 13:49
· Личное сообщение · #18

Fallout пишет:
PORTA.0 = 1;


Да, это знаю, но устанавливать нужно не бит порта, а бит в переменной.

s0larian пишет:
Это делается проще через struct и bit fields


Вот как раз из всего Си, (а я писал, что только учу его), со структурами я ничего и не понял...
Подчитаю ещё. Спасибо за наводку !

reversecode пишет:
проще уже смотреть как компилятор соптимизирует на asm


Да жуть что он делает. Он зависим от конкретных значений и констант. Одну из приведенных строчек он делает в две команды, на другой десяток строк и несколько подпрограмм.


Я вот уперся в реализацию универсальной и оптимальной функции на Си, которая в итоге должна сделать лишь перетасовку данных с порта в соответствующие биты 2-х байтовых переменных. Если взглянуть на картину целиком, то получается всё очень просто на ассемблере если делать всё скопом:

clr zl
clr zh
sbis pinb,popin
sbr zl,bitpokey
sbis pinb,lnpin
sbr zl,bitlnkey
sbis pinb,sl1pin
sbr zl,bitsl1key
sbis pinb,atpin
sbr zh,bitatkey
sbis pinb,mnpin
sbr zh,bitmnkey
sbis pinb,cnpin
sbr zh,bitcnkey
sbis pinb,st2pin
sbr zh,bitst2key
sbis pind,sl3pin
sbr zl,bitsl3key
sbis pind,st1pin
sbr zh,bitst1key
sbis pind,btpin
sbr zl,bitbtkey
sbis pind,sl2pin
sbr zl,bitsl2key
sbis pinb,stpin
sbr zl,bitstkey


Единственное не разобрался ещё как передавать в CodeVision ассемблерным коммандам константы объявленные в #define. Хотя при желании всё можно заменить конкретными цифрами и номерами регистров, но это будет не наглядно.

Fallout пишет:
п.с: вообще заметил тема МК тут появляться уже не раз что радует


Да, творческие идеи рвутся наружу за пределы громоздких PC... Каждая программа хочет жить своей жизнью и не зависеть от Билла...




Ранг: 387.4 (мудрец)
Активность: 0.170
Статус: Участник
системщик

Создано: 29 апреля 2008 20:19
· Личное сообщение · #19

ToBad, вот тебе целый пример для VC++:


#pragma pack(push)
#pragma pack(1)
struct Uart
{
byte rx_fifo;
byte flag1:1,
flag2:1,
flag3:1,
reserved:5;
byte tx_fifo;
};
#pragma pack(pop)

volatile struct Uart *g_uart = (struct Uart *)0x80001010;

void init()
{
const char *prompt = "Hello\r\n";
byte i;

g_uart->flag1 = 1; // Enable something...
g_uart->flag2 = 0; // set something else...

for (i = 0; i < strlen(prompt); ++i)
g_uart->tx_fifo = prompt[i];
}





Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 01 мая 2008 00:47
· Личное сообщение · #20

Всем спасибо за помощь ! Со структурами тоже разобрался. Удобно, по коду переназначение битов одного байта одной структуры в другую 81 слово занимает, что меньше варианта с логикой. Так же у CodeVision есть своя работа с типом bit. По размеру это ещё не проверял. Пока помогла вставка на асме.



Ранг: 37.4 (посетитель)
Активность: 0.010
Статус: Участник

Создано: 04 мая 2008 04:37
· Личное сообщение · #21

Если таких конструкций много, то может быть удастся копировать сразу несколько бит?



Ранг: 203.3 (наставник)
Активность: 0.220
Статус: Участник
UPX Killer -d

Создано: 05 мая 2008 16:30
· Личное сообщение · #22

Возможно, есть смысл присомтреться к одной особенности ассемблера. Чисто редположение...
В масме например не прокатывает инструкция типа shr eax, edx или eax, dl, потому как типа надо константу указывать
Зато прокатывает shr eax, cl
Может быть, и в том асме есть подобные особенности...

-----
Я медленно снимаю с неё UPX... *FF_User*



 eXeL@B —› Программирование —› Помогите оптимизировать манипуляцию с битами на Си
:: Ваш ответ
Жирный  Курсив  Подчеркнутый  Перечеркнутый  {mpf5}  Код  Вставить ссылку 
:s1: :s2: :s3: :s4: :s5: :s6: :s7: :s8: :s9: :s10: :s11: :s12: :s13: :s14: :s15: :s16:


Максимальный размер аттача: 500KB.
Ваш логин: german1505 » Выход » ЛС
   Для печати Для печати