Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: отследить изменение переменной.
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Jenya7
есть пакет с данными uint8_t packet[20] . переменная uint32_t занимает 4 байта. соответственно если я хочу посмотреть изменилась ли переменная в следующей посылке я могу сделать так
Код
if (new_packet[0] != old_packet[0] || new_packet[1] != old_packet[1] || new_packet[2] != old_packet[2] || new_packet[3] != old_packet[3])

или так
Код
if(memcmp(new_packet, old_packet, 4) != 0)

вопрос какой способ быстрее?
zltigo
QUOTE (Jenya7 @ Dec 15 2015, 12:14) *
вопрос какой способ быстрее?

Один другого хреновее. Сравнивать надо сразу 32bit значения. Типы преобразуйте и все.



Jenya7
Цитата(zltigo @ Dec 15 2015, 16:18) *
Один другого хреновее. Сравнивать надо сразу 32bit значения. Типы преобразуйте и все.

так что ли?

if(*((uint32_t*)new_packet) != *((uint32_t*)old_packet))

а что, за кулисами происходит не одно и то же?
Dog Pawlowa
Ну, для начала желательно знать, что за ядро.
То, что предложил ув. zltigo, имеет смысл только для 32-битного ядра.
msalov
Цитата(Jenya7 @ Dec 15 2015, 14:09) *
так что ли?

if(*((uint32_t*)new_packet) != *((uint32_t*)old_packet))

а что, за кулисами происходит не одно и то же?


Нет, разное происходит. При таком варианте у вас может быть проблема с невыровненным доступом.
Идеальный вариант - объявить упакованную структуру. которая разложит байты пакета согласно логического содержания, ну а дальше оперировать полями этой структуры.

Код
   __packed struct packet {
       ...
       uint32_t ref;
       ...
   };
  
   struct packet *old_packet;
   struct packet *new_packet;
  
   if (old_packet->ref != new_packet->ref)
   {
       do_stuff();
   }

__packed зависит от вашего компилятора.
Jenya7
Цитата(Dog Pawlowa @ Dec 15 2015, 17:32) *
Ну, для начала желательно знать, что за ядро.
То, что предложил ув. zltigo, имеет смысл только для 32-битного ядра.

это Cortex-M3

Цитата(msalov @ Dec 15 2015, 17:33) *
Нет, разное происходит. При таком варианте у вас может быть проблема с невыровненным доступом.
Идеальный вариант - объявить упакованную структуру. которая разложит байты пакета согласно логического содержания, ну а дальше оперировать полями этой структуры.

Код
__packed struct packet {
       ...
       uint32_t ref;
       ...
   };
  
   struct packet *old_packet;
   struct packet *new_packet;
  
   if (old_packet->ref != new_packet-<ref)
   {
       do_stuff();
   }

__packed зависит от вашего компилятора.


функция принимает массив uint8_t . к тому же что может быть невыравненно в массиве?
ок. допустим. все равно я думаю это будут те же операции на уровне ассемблера.
msalov
Цитата(Jenya7 @ Dec 15 2015, 14:37) *
функция принимает массив uint8_t . к тому же что может быть невыравненно в массиве?

Адрес 0x0F сгодится для указателя на uint8_t, но не сгодится для uint32_t.

Цитата(Jenya7 @ Dec 15 2015, 14:37) *
ок. допустим. все равно я думаю это будут те же операции на уровне ассемблера.

Будущему вам будет проще читать
Код
if (old_packet->ref != new_packet-<ref)

нежели
Код
if (new_packet[0] != old_packet[0] || new_packet[1] != old_packet[1] || new_packet[2] != old_packet[2] || new_packet[3] != old_packet[3])

да и ошибку допустить будет сложнее в первом случае, вот и вся премудрость.
Jenya7
Цитата(msalov @ Dec 15 2015, 17:41) *
Адрес 0x0F сгодится для указателя на uint8_t, но не сгодится для uint32_t.

допустим первая переменная – uint8_t , вторая – uint8_t , третья – uint32_t тогда
if(*((uint32_t*)&new_packet[2]) != *((uint32_t*)&old_packet[2]))
и так далее.
zltigo
QUOTE (Dog Pawlowa @ Dec 15 2015, 13:32) *
Ну, для начала желательно знать, что за ядро.

Надо, но не по другой причите о которой тоже сказали.
QUOTE
То, что предложил ув. zltigo, имеет смысл только для 32-битного ядра.

Для любого имеет, ибо сравнение 32bit на любой платформе это встроенная фича компилятора, которая реализована МАКСИМАЛЬНО хорошо.


QUOTE (msalov @ Dec 15 2015, 13:41) *
Адрес 0x0F сгодится для указателя на uint8_t, но не сгодится для uint32_t.

НАЧАЛО массива по любому будет выравнено.
Но в общеем случае, то есть ВСЕГДА sm.gif, нужно преобразовывать к указателю на структуру и работать с элементеми.
Jenya7
Цитата(zltigo @ Dec 15 2015, 17:54) *
Надо, но не по другой причите о которой тоже сказали.

Для любого имеет, ибо сравнение 32bit на любой платформе это встроенная фича компилятора, которая реализована МАКСИМАЛЬНО хорошо.



НАЧАЛО массива по любому будет выравнено.
Но в общеем случае, то есть ВСЕГДА sm.gif, нужно преобразовывать к указателю на структуру и работать с элементеми.

так какой метод МАКСИМАЛЬНО хорош по вашему мнению?
меня интересует СКОРОСТЬ исполнения. sm.gif
zltigo
QUOTE (Jenya7 @ Dec 15 2015, 13:45) *
допустим первая переменная – uint8_t , вторая – uint8_t , третья – uint32_t тогда
if(*((uint32_t*)&new_packet[2]) != *((uint32_t*)&old_packet[2]))
и так далее.

Нет, это фигня. В общем случае НЕЛЬЗЯ.



QUOTE (Jenya7 @ Dec 15 2015, 13:56) *
так какой метод МАКСИМАЛЬНО хорош по вашему мнению?
меня интересует СКОРОСТЬ исполнения. sm.gif

Тот о котором написано, что так надо делать ВСЕГДА. Что можно было не понять?
Jenya7
Цитата(zltigo @ Dec 15 2015, 18:00) *
Нет, это фигня. В общем случае НЕЛЬЗЯ.

Тот о котором написано, что так надо делать ВСЕГДА. Что можно было не понять?

а как не фигня?
какая структура. у меня функция принимает массив uint8_t.

Код
tBleStatus aci_gatt_update_char_value(uint16_t servHandle,
                      uint16_t charHandle,
                      uint8_t charValOffset,
                      uint8_t charValueLen,  
                      const uint8_t *charValue)

вот сюда - uint8_t *charValue - я должен передать свой пакет.
и для сравнения я получаю его как uint8_t *charValue а не как структуру
XVR
Цитата
какая структура. у меня функция принимает массив uint8_t.
Ну так преобразуйте в структуру. На что в С дано приведение типов? sm.gif
zltigo
QUOTE (Jenya7 @ Dec 15 2015, 14:07) *
какая структура. у меня функция принимает массив uint8_t.

1) Не массив, а УКАЗАТЕЛЬ на массив
2) Преобразовать этот указатель в указатель на пакованную структкру и работать с ЭЛЕМЕНТАМИ этой сруктуры.
3) Что Вас удивило со структурой, если поняли, что преобразовывать указатель на массив в указатель на long можно?


Jenya7
Цитата(zltigo @ Dec 15 2015, 18:18) *
1) Не массив, а УКАЗАТЕЛЬ на массив
2) Преобразовать этот указатель в указатель на пакованную структкру и работать с ЭЛЕМЕНТАМИ этой сруктуры.
3) Что Вас удивило со структурой, если поняли, что преобразовывать указатель на массив в указатель на long можно?

и все это для того чтобы сравнить неколько переменных? мне кажется быстрее уж сделать тем же memcmp
zltigo
QUOTE (Jenya7 @ Dec 15 2015, 14:22) *
и все это для того чтобы сравнить неколько переменных? мне кажется быстрее уж сделать тем же memcmp

Ну и каша у Вас с голове sad.gif. Рано Вам реальным программирванием заниматься sad.gif если не понимаете разницы между преобразованиями типов данных и работой данными. "Все это" НИ ОДНОГО БИТА КОДА НЕ ДОБАВЛЯЕТ. "Вcе это" только ИНСТУКЦИИ компилятору, как ему МАКСИМАЛЬНО эффективно сделать работу при ОНОВРЕМЕННОМ обеспечении читаемомти человеком.
msalov
Цитата(Jenya7 @ Dec 15 2015, 15:22) *
и все это для того чтобы сравнить неколько переменных? мне кажется быстрее уж сделать тем же memcmp


Программа пишется не для компилятора, а в первую очередь для того, кто будет её читать в будущем. Возможно и для будущего вас.
Jenya7
а что можно прямо так ?
(my_struct *) my_arr
zltigo
QUOTE (Jenya7 @ Dec 15 2015, 15:39) *
а что можно прямо так ?
(my_struct *) my_arr

Да, конечно. Преобразование типа говорящее компилятору, что там по указателю на самом деле не свалка байтов, а структура. В структуре находися какая-то 32 bit переменая. Осталось только сказать компилятору что бы взял эту переменную.

((my_struct *) my_arr)->value_32bit

И сделает от это МАСИМАЛЬНО эффективным способом.
Для человеческой читабельности можно спрятать преобразование в макрос, или промежуточную переменную, которую компилятор все равно заоптимизирует.
Получится типа:

my_struct->value_32bit
Jenya7
понял.спасибо.
Jenya7
сделал листинг ради интереса.
Код
if (new_packet[4] != old_packet[4] || new_packet[5] != old_packet[5] || new_packet[6] != old_packet[6] || new_packet[7] != old_packet[7])

    1db8:    7938          ldrb    r0, [r7, #4]
    1dba:    7933          ldrb    r3, [r6, #4]
    1dbc:    4298          cmp    r0, r3
    1dbe:    d10b          bne.n    1dd8 <PARSER_ParseBlePacket+0x458>
    1dc0:    797a          ldrb    r2, [r7, #5]
    1dc2:    7971          ldrb    r1, [r6, #5]
    1dc4:    428a          cmp    r2, r1
    1dc6:    d107          bne.n    1dd8 <PARSER_ParseBlePacket+0x458>
    1dc8:    79bd          ldrb    r5, [r7, #6]
    1dca:    79b0          ldrb    r0, [r6, #6]
    1dcc:    4285          cmp    r5, r0
    1dce:    d103          bne.n    1dd8 <PARSER_ParseBlePacket+0x458>
    1dd0:    79fa          ldrb    r2, [r7, #7]
    1dd2:    79f3          ldrb    r3, [r6, #7]
    1dd4:    429a          cmp    r2, r3
    1dd6:    d010          beq.n    1dfa <PARSER_ParseBlePacket+0x47a>


    if(*((uint32_t*)&new_packet[4]) != *((uint32_t*)&old_packet[4]))

    1db8:    6878          ldr    r0, [r7, #4]
    1dba:    6873          ldr    r3, [r6, #4]
    1dbc:    4298          cmp    r0, r3
    1dbe:    d010          beq.n    1de2 <PARSER_ParseBlePacket+0x462>


    if(memcmp(&new_packet[4],&old_packet[4],4) != 0)

    1a48:    1d3c          adds    r4, r7, #4
    1a4a:    4620          mov    r0, r4
    1a4c:    1d31          adds    r1, r6, #4
    1a4e:    2204          movs    r2, #4
    1a50:    f005 f8f8     bl    6c44 <memcmp>
    1a54:    b160          cbz    r0, 1a70 <PARSER_ParseBlePacket+0xf0>


if(*((uint32_t*)&new_packet[4]) != *((uint32_t*)&old_packet[4])) рулит. в принципе это частный случай более общего - ((my_struct *) my_arr)->value_32bit

кстати я тут подумал
такую запись
Код
tempParam.maxFlowRate = (uint32_t)new_packet[0]+((uint32_t)new_packet[1]<<8)+((uint32_t)new_packet[2]<<16)+((uint32_t)new_packet[3]<<24);


можно заменить на
Код
tempParam.maxFlowRate = *((uint32_t*)&new_packet[0]);
или
tempParam.maxFlowRate = ((my_struct *) my_arr)->value_32bit


или я не прав?

вот листинг
Код
tempParam.maxFlowRate = (uint32_t)new_packet[3]+((uint32_t)new_packet[2]<<8)+((uint32_t)new_packet[1]<<16)+((uint32_t)new_packet[0]<<24);
    1e32:    7878          ldrb    r0, [r7, #1]
    1e34:    78bc          ldrb    r4, [r7, #2]
    1e36:    0403          lsls    r3, r0, #16
    1e38:    78fa          ldrb    r2, [r7, #3]
    1e3a:    eb03 2104     add.w    r1, r3, r4, lsl #8
    1e3e:    7838          ldrb    r0, [r7, #0]
    1e40:    188c          adds    r4, r1, r2
    1e42:    eb04 6300     add.w    r3, r4, r0, lsl #24
    1e46:    9317          str    r3, [sp, #92]; 0x5c

tempParam.maxFlowRate = *((uint32_t*)&new_packet[0]);
    1998:    683b          ldr    r3, [r7, #0]
    199c:    9317          str    r3, [sp, #92]; 0x5c

нехилая разница.
zltigo
QUOTE (Jenya7 @ Dec 16 2015, 10:45) *
можно заменить на

Не можно, а НУЖНО. Нужно давать компилятору нормальные максимально обобщенные задания а не заставлять его жонглировать байтами и битами. Грубо говоря надо стараться давать задание "выкопай яму", а не "возьми лопату, воткни в землю, бери больше, кидай дальше...повтори...". Для этого надо в первую очередь знать язык, ибо на подмножестве языка уровня Эллочки Людоедки, объяснить компилятору нормально не реально.
XVR
Цитата
if(*((uint32_t*)&new_packet[4]) != *((uint32_t*)&old_packet[4])) рулит. в принципе это частный случай более общего - ((my_struct *) my_arr)->value_32bit
На ARM этот 'частный случай' даст hard fault если адрес new_packet или old_packet не будет кратен 4, а так да, оно рулит sm.gif

zltigo
QUOTE (XVR @ Dec 16 2015, 13:09) *
На ARM этот 'частный случай' даст hard fault...

Не обязательно fault - чаще фигню взятую по выровненному адресу молча возвратит. Зависит от конкретной реализации в ядре сделанной конкретным производителем чипа. Но то, что получится нерабочий код это точно.
CrimsonPig
Цитата(zltigo @ Dec 16 2015, 11:18) *
Не обязательно fault - чаще фигню взятую по выровненному адресу молча возвратит. Зависит от конкретной реализации в ядре сделанной конкретным производителем чипа. Но то, что получится нерабочий код это точно.


Таки коллективный разум все еще считает себя умнее и быстрее правильно заинлайненного memcmp ? sm.gif
zltigo
QUOTE (CrimsonPig @ Dec 16 2015, 17:00) *
sm.gif

К чему эта улыбочка? Для начала попробуйте сделать этот самый "правильно заинлайненый". Для начала этого сделать просто не удастся - это библиотечная функция. Посему посморите просто на исходник memcmp() дабы, даже если ее заинлайтить, понять написанную Вами глупость.
CrimsonPig
Цитата(zltigo @ Dec 16 2015, 15:08) *
К чему эта улыбочка? Для начала попробуйте сделать этот самый "правильно заинлайненый". Для начала этого сделать просто не удастся - это библиотечная функция. Посему посморите просто на исходник memcmp() дабы, даже если ее заинлайтить, понять написанную Вами глупость.


Я делаю так:
1. пишу memcmp
2. компилирую в release c соответствующими опциями оптимизации.
3. все

Например, такая вот глупая функция:
int Foo_cmp(const void* apSrc, size_t aNumBytes)
{
unsigned char buf[100] = "abcdef";
return memcmp(apSrc, buf, aNumBytes);
}

компилируется в следующий кусок ассемблерной хрени:
CODE

return memcmp(apSrc, buf, aNumBytes);
0134103E mov esi,0Fh
01341043 lea ecx,[ebp-68h]
01341046 mov edx,offset string "qweqweqweq" (13420FCh)
0134104B jmp Foo_cmp+50h (1341050h)
0134104D lea ecx,[ecx]
01341050 mov eax,dword ptr [edx]
01341052 cmp eax,dword ptr [ecx]
01341054 jne Foo_cmp+68h (1341068h)
01341056 sub esi,4
01341059 add ecx,4
0134105C add edx,4
0134105F cmp esi,4
01341062 jae Foo_cmp+50h (1341050h)
01341064 test esi,esi
01341066 je Foo_cmp+0B9h (13410B9h)
01341068 movzx eax,byte ptr [edx]
0134106B movzx edi,byte ptr [ecx]
0134106E sub eax,edi
01341070 jne Foo_cmp+0A3h (13410A3h)
01341072 cmp esi,1
01341075 jbe Foo_cmp+0B9h (13410B9h)
01341077 movzx eax,byte ptr [edx+1]
0134107B movzx edi,byte ptr [ecx+1]
0134107F sub eax,edi
01341081 jne Foo_cmp+0A3h (13410A3h)
01341083 cmp esi,2
01341086 jbe Foo_cmp+0B9h (13410B9h)
01341088 movzx eax,byte ptr [edx+2]
0134108C movzx edi,byte ptr [ecx+2]
01341090 sub eax,edi
01341092 jne Foo_cmp+0A3h (13410A3h)
01341094 cmp esi,3
01341097 jbe Foo_cmp+0B9h (13410B9h)
01341099 movzx eax,byte ptr [edx+3]
0134109D movzx ecx,byte ptr [ecx+3]
013410A1 sub eax,ecx
013410A3 sar eax,1Fh
013410A6 pop edi
013410A7 or eax,1
013410AA pop esi


В debug версии да, стоит честный вызов memcmp
XVR
Цитата(zltigo @ Dec 16 2015, 18:08) *
Для начала попробуйте сделать этот самый "правильно заинлайненый". Для начала этого сделать просто не удастся - это библиотечная функция.
Не всегда - у gcc например это может быть intrinsic функция компилятора (если ему включить оптимизацию, конечно)


zltigo
QUOTE (XVR @ Dec 16 2015, 17:30) *
Не всегда - у gcc например это может быть intrinsic функция компилятора (если ему включить оптимизацию, конечно)

Договорились. Пререходим к следующему шагу - используем GCC и его intrinsic, получаем результат и сравниваем с:
CODE
if(*((uint32_t*)&new_packet[4]) != *((uint32_t*)&old_packet[4]))
    1db8:    6878          ldr    r0, [r7, #4]
    1dba:    6873          ldr    r3, [r6, #4]
    1dbc:    4298          cmp    r0, r3


CrimsonPig
Цитата(zltigo @ Dec 16 2015, 15:38) *
Договорились. Пререходим к следующему шагу - используем GCC и его intrinsic, получаем результат и сравниваем с:


memcmp - то гарантированно работает с невыравненными данными.
Если есть 100% гарантия, что данные выравнены, то и заинлайненная memcmp из библиотеки будет на 3 инструкции.
zltigo
QUOTE (CrimsonPig @ Dec 16 2015, 17:25) *
Например, такая вот глупая функция:

Не надо сравнивать несравнимые вещи. Посему прошу говорить об одном и том-же, а не подсовывать вместо теплого мягкое. Напишите и сравните результаты ИСХОДНОГО выражения:
CODE
if(*((uint32_t*)&new_packet[4]) != *((uint32_t*)&old_packet[4]))

и Вашего варианита с memcpy()


QUOTE (CrimsonPig @ Dec 16 2015, 17:43) *
memcmp - то гарантированно работает с невыравненными данными.

Работа с элементом стуктуры так-же 100% работает с невыравненными данными. Ну и?

XVR
gcc 4.8.2 не знает как инлайнить memcmp ( http://gcc.godbolt.org/ ), увы sad.gif

Если поииследовать memcpy (хотя она тут как телеге пятое колесо), то
memcpy для выровненных данных заинлайнил:
Код
#include <memory.h>

void do_memcmp(void* p1, const void* p2)
{
  memcpy(__builtin_assume_aligned(p1,4),__builtin_assume_aligned(p2,4),4);
}

сделал
Код
do_memcmp(void const*, void const*):
        ldr     r3, [r1]
        str     r3, [r0]
        bx      lr

невыровненный тоже сделал:
Код
#include <memory.h>

void do_memcmp(void* p1, const void* p2)
{
  memcpy(p1,p2,4);
}

Код
do_memcmp(void*, void const*):
        ldr     r3, [r1]  @ unaligned
        str     r3, [r0]  @ unaligned
        bx      lr

Остался только вопрос - под какой это камень sm.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.