Лучше не использовать типы char в stm32?
- COKPOWEHEU
- Говорящий с текстолитом
- Сообщения: 1525
- Зарегистрирован: Чт июн 10, 2010 20:11:19
Re: Лучше не использовать типы char в stm32?
[uquote="AVI-crak",url="/forum/viewtopic.php?p=4109715#p4109715"]Логика очень простая - новые технологии экономят флеш, и значительно повышают быстродействие.[/uquote]
А какие из технологий здесь вы считаете новыми? Если оптимизатор компилятора, то он одинаковый в обоих случаях. А использованные алгоритмы преобразования в строку не менялись с каких-то допотопных времен.
А какие из технологий здесь вы считаете новыми? Если оптимизатор компилятора, то он одинаковый в обоих случаях. А использованные алгоритмы преобразования в строку не менялись с каких-то допотопных времен.
- VladislavS
- Собутыльник Кота
- Сообщения: 2562
- Зарегистрирован: Вт май 01, 2018 19:44:47
Re: Лучше не использовать типы char в stm32?
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4109570#p4109570"]
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4109706#p4109706"]Пока что единственное, что нам известно - что мой код в занимает в 1.6 раз меньше места в ПЗУ. Поскольку никаких кэширований и других оптимизаций на скорость не использовалось, логично предположить, что он будет быстрее выполняться.[/uquote]Дарю самый быстрый в мире код. Одна ассемблерная команда будет всего.
При том, что именно эта ветка дискуссии была про AVR, естественно.[/uquote]Если на клетке слона прочтёшь надпись «буйвол», не верь глазам своим. Козьма ПрутковНе понял - а при чём тут AVR если тема про ARM?
Код: Выделить всё
for(;;);- COKPOWEHEU
- Говорящий с текстолитом
- Сообщения: 1525
- Зарегистрирован: Чт июн 10, 2010 20:11:19
Re: Лучше не использовать типы char в stm32?
Все-таки проверил скорость работы. Правда, под рукой оказался только древний AVRStudio, у которого далеко не лучшая оптимизация. Ну да для грубой оценки сойдет.
Вариант jcxz выполняется за 10180 тактов.
Вариант мой - за 11116
Результат: вариант jcxz чуть быстрее.
Для моих задач (обычно это отладочный вывод на UART или дисплей с человеко-читаемой скоростью) эта разница не критична, и я скорее всего продолжу пользоваться своим вариантом, как более читаемым.
Еще, наверное, проверю на ARM-M3, поскольку такие камни у меня есть, и поскольку аппаратной математики у них побольше, чем у AVR, с которых мы начинали.
Вариант jcxz выполняется за 10180 тактов.
Вариант мой - за 11116
Результат: вариант jcxz чуть быстрее.
Для моих задач (обычно это отладочный вывод на UART или дисплей с человеко-читаемой скоростью) эта разница не критична, и я скорее всего продолжу пользоваться своим вариантом, как более читаемым.
Еще, наверное, проверю на ARM-M3, поскольку такие камни у меня есть, и поскольку аппаратной математики у них побольше, чем у AVR, с которых мы начинали.
- Eddy_Em
- Собутыльник Кота
- Сообщения: 2516
- Зарегистрирован: Пт июл 12, 2019 22:52:01
- Контактная информация:
Re: Лучше не использовать типы char в stm32?
COKPOWEHEU, а как насчет на F0 проверить?
Что-то меня гложут сомнения, что операции над uint64_t будут выполняться быстрей, чем деление (пусть и софтовое) на 10 uint32_t.
Что-то меня гложут сомнения, что операции над uint64_t будут выполняться быстрей, чем деление (пусть и софтовое) на 10 uint32_t.
- COKPOWEHEU
- Говорящий с текстолитом
- Сообщения: 1525
- Зарегистрирован: Чт июн 10, 2010 20:11:19
Re: Лучше не использовать типы char в stm32?
Мне неначем: ни симулятора, ни железки нет.COKPOWEHEU, а как насчет на F0 проверить?
---
А вот на stm32L151 не удержался и проверил. Попутно оказалось, что обе наши реализации работают неправильно. Заодно ради эксперимента привел код jcxz к своему чтобы не пришлось в конце переворачивать строку.
Исправленный вариант jcxz:
Спойлер
Код: Выделить всё
#define B35 (1ULL<<35)
char* utoa10(uint32_t x, char *str){
#define D ((B35 + 5) / 10)
uint32_t i;
char *s1, *s = str;
do{
x = (i=x) * D >> 35;
*(s++) = i - x * 10 + '0';
}while(x);
s[0] = 0;
s1 = str;
while((uint32_t)--s > (uint32_t)str){
i = s[0];
s[0] = str[0];
*(str++) = i;
}
return s1;
#undef D
}
Исправленный мой вариант:
Спойлер
Код: Выделить всё
char* u32toa(uint32_t val, char *buf){
buf+=11;
buf[0] = 0;
do{
buf--;
buf[0] = val % 10;
val /= 10;
if(buf[0] < 10)buf[0] += '0'; else buf[0] = buf[0] - 0x0A + 'A';
}while(val);
return buf;
}
Гибридный вариант:
Спойлер
Код: Выделить всё
#define B35 (1ULL<<35)
char* utoa10_2(uint32_t x, char *str){
#define D ((B35 + 5) / 10)
uint32_t i;
char *s = str+11;
s[0] = 0;
do{
x = (i=x) * D >> 35;
*(--s) = i - x * 10 + '0';
}while(x);
return s;
#undef D
}
Результат: исходный вариант jcxz выполняется уже чуть дольше моего, скорее всего из-за того, что приходится в конце переворачивать строку. Если заполнять массив справа налево, выполняется уже быстрее.
Re: Лучше не использовать типы char в stm32?
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4109799#p4109799"]А вот на stm32L151 не удержался и проверил.[/uquote]
На M3 и выше, как я уже говорил, компилятор сам заменяет деление умножением, при том что там есть и аппаратное деление с которым результат будет не особо хуже, а вот на М0 будет использоваться софтовое деление потому твой вариант с делением и взятием остатка - это один из самых медленных подходов.
На M3 и выше, как я уже говорил, компилятор сам заменяет деление умножением, при том что там есть и аппаратное деление с которым результат будет не особо хуже, а вот на М0 будет использоваться софтовое деление потому твой вариант с делением и взятием остатка - это один из самых медленных подходов.
- COKPOWEHEU
- Говорящий с текстолитом
- Сообщения: 1525
- Зарегистрирован: Чт июн 10, 2010 20:11:19
Re: Лучше не использовать типы char в stm32?
Пруфы где? Пока я вижу, что разница незначительная, причем иногда даже в пользу моего варианта.потому твой вариант с делением и взятием остатка - это один из самых медленных подходов.
- AVI-crak
- Прорезались зубы
- Сообщения: 202
- Зарегистрирован: Сб янв 09, 2016 15:51:17
- Контактная информация:
Re: Лучше не использовать типы char в stm32?
Напомню что язык Си совершенно бесплатно даёт возможность отстрелить себе все конечности. У нас именно такой случай.
В идеале нужно обвесить функцию проверками размера стека, так чтобы не отсвечивали. Меня хватило на передачу хвоста буфера - защита на минималках.
Кроме того меня нервирует пропавшее второе умножение, вместо которого используется три строчки сдвигов и сложений.
В идеале нужно обвесить функцию проверками размера стека, так чтобы не отсвечивали. Меня хватило на передачу хвоста буфера - защита на минималках.
Кроме того меня нервирует пропавшее второе умножение, вместо которого используется три строчки сдвигов и сложений.
Re: Лучше не использовать типы char в stm32?
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4109811#p4109811"]Пруфы где?[/uquote]
Берем STM32G0(M0+) и тестим(-O2) с твоей оригинальной функцией, затем убираем получение остатка от деления и наконец заменяем само деление:
Последний вариант быстрее в 2.7 раза, хотя разница падает по мере уменьшения передаваемого значения, т.к. софтовое деление тогда завершается быстрее. Если числа порядка 1000, то первый и последний вариант выдают сравнимые значения, в среднем наверное второй вариант самый оптимальный, тем более так можно не только десятичные числа выводить.
Берем STM32G0(M0+) и тестим(-O2) с твоей оригинальной функцией, затем убираем получение остатка от деления и наконец заменяем само деление:
Спойлер
Код: Выделить всё
char* u32toa(uint32_t val, char* buf)
{
buf += 11;
*buf = 0;
do
{
buf--;
buf[0] = val % 10;
val /= 10;
if (buf[0] < 10) buf[0] += '0';
else buf[0] -= 0x0A + 'A';
} while (val);
return buf;
}
char* u32toa2(uint32_t val, char* buf)
{
buf += 11;
*buf = 0;
do
{
buf--;
uint32_t tmp = val;
val /= 10;
*buf = tmp - val * 10;
if (*buf < 10) *buf += '0';
else *buf -= 0x0A + 'A';
} while (val);
return buf;
}
uint32_t divu10(uint32_t val) { return val * (((1ULL << 35) + 5) / 10) >> 35; }
char* u32toa3(uint32_t val, char* buf)
{
buf += 11;
*buf = 0;
do
{
buf--;
uint32_t tmp = val;
val = divu10(val);
*buf = tmp - val * 10;
if (*buf < 10) *buf += '0';
else *buf -= 0x0A + 'A';
} while (val);
return buf;
}
..........
volatile uint32_t val = 4294967295;
PerfCounter pc;
pc.start();
for (uint32_t i = 100; i > 0; i--)
{
u32toa(val, buf); // 258'906
//u32toa2(val, buf); // 138'506
//u32toa3(val, buf); // 95'713
}
auto t = pc.getElapsedTicks();
rtt.println(t);
- Eddy_Em
- Собутыльник Кота
- Сообщения: 2516
- Зарегистрирован: Пт июл 12, 2019 22:52:01
- Контактная информация:
Re: Лучше не использовать типы char в stm32?
Reflector, а зачем там кусок от вывода шестнадцатеричных остался? `else *buf -= 0x0A + 'A';` не будет выполняться.
А выводить шеснадцатеричные числа значительно проще, там никаких делений не нужно:
А выводить шеснадцатеричные числа значительно проще, там никаких делений не нужно:
Код: Выделить всё
void printuhex(uint32_t val){
addtobuf("0x");
uint8_t *ptr = (uint8_t*)&val + 3;
int i, j, z = 1;
for(i = 0; i < 4; ++i, --ptr){
if(*ptr == 0){ // omit leading zeros
if(i == 3) z = 0;
if(z) continue;
}
else z = 0;
for(j = 1; j > -1; --j){
uint8_t half = (*ptr >> (4*j)) & 0x0f;
if(half < 10) bufputchar(half + '0');
else bufputchar(half - 10 + 'a');
}
}
}Re: Лучше не использовать типы char в stm32?
[uquote="Eddy_Em",url="/forum/viewtopic.php?p=4109850#p4109850"]а зачем там кусок от вывода шестнадцатеричных остался?[/uquote]
Я для теста взял функцию COKPOWEHEU, главное что там деление и взятие остатка есть.
Я для теста взял функцию COKPOWEHEU, главное что там деление и взятие остатка есть.
- COKPOWEHEU
- Говорящий с текстолитом
- Сообщения: 1525
- Зарегистрирован: Чт июн 10, 2010 20:11:19
Re: Лучше не использовать типы char в stm32?
Разверните свою мысль чтобы понятна была. Какой случай? При чем здесь стек? Через стек передаются только uint32_t в качестве аргумента и char* (обычно uint16_t либо uint32_t) в качестве буфера. Уж 6-8 свободных байт в стеке скорее всего найдется.Напомню что язык Си совершенно бесплатно даёт возможность отстрелить себе все конечности. У нас именно такой случай.
В идеале нужно обвесить функцию проверками размера стека,
Да, именно это мне и нужно, что ж вы изворачиваетесь-то постоянно от прямого вопроса! Где тесты? Опять от вас одни "логические соображения".Берем STM32G0(M0+) и тестим(-O2)
В общем, пока пруфов нет, остается пользоваться моими данными: деление через B35 чуть-чуть быстрее, но менее читаемое. И, естественно, буфер надо заполнять с конца.
Re: Лучше не использовать типы char в stm32?
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4109858#p4109858"]Да, именно это мне и нужно, что ж вы изворачиваетесь-то постоянно от прямого вопроса! Где тесты? Опять от вас одни "логические соображения".[/uquote]
Смотри, есть такой код:
Если посмотреть что генерит gcc для M0, то там будут вызовы функций __aeabi_uidivmod() и __udivsi3(), при это первая дополнительно вызывает вторую, т.е. деление выполняется дважды о чем тебе уже говорил jcxz и я об этом также знал еще когда писал свои функции конвертации, так что это никакие не чисто "логические соображения". Более того я написал три разных функции, протестил их и результаты спрятал под спойлер, но видимо спрятал слишком хорошо, раз не все смогли с ними ознакомиться 
Смотри, есть такой код:
Код: Выделить всё
buf[0] = val % 10;
val /= 10;- COKPOWEHEU
- Говорящий с текстолитом
- Сообщения: 1525
- Зарегистрирован: Чт июн 10, 2010 20:11:19
Re: Лучше не использовать типы char в stm32?
Три разных функции это хорошо, жаль вы не выложили по ним результаты.я написал три разных функции, протестил их и результаты
Там под спойлером есть какие-то чиселки, но без контекста они никакого смысла не несут.
Деление выполняется единажды, о чем я уже писал. Получение частного и остатка это одна функция.т.е. деление выполняется дважды
Re: Лучше не использовать типы char в stm32?
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4109887#p4109887"]Три разных функции это хорошо, жаль вы не выложили по ним результаты.
Там под спойлером есть какие-то чиселки, но без контекста они никакого смысла не несут.[/uquote]
Под спойлером есть все, что нужно, разве что класс замеряющий время выполнения придется чем-то заменить, если хочется мерять самому. Я даже на всякий случай проверил насколько точно измеряет и для nop<500>(), т.е. 500 подставленных NOP получил ровно 500 тиков.
Там под спойлером есть какие-то чиселки, но без контекста они никакого смысла не несут.[/uquote]
Под спойлером есть все, что нужно, разве что класс замеряющий время выполнения придется чем-то заменить, если хочется мерять самому. Я даже на всякий случай проверил насколько точно измеряет и для nop<500>(), т.е. 500 подставленных NOP получил ровно 500 тиков.
Ну давай считать:COKPOWEHEU писал(а):Деление выполняется единажды, о чем я уже писал. Получение частного и остатка это одна функция.
Код: Выделить всё
buf[0] = val % 10;
0x20001ec4 movs r1, #10
0x20001ec6 movs r0, r5
0x20001ec8 bl 0x20001824 <__aeabi_uidivmod>
val /= 10;
0x20001ecc movs r0, r5
0x20001ece uxtb r4, r1
0x20001ed0 movs r1, #10
0x20001ed2 bl 0x20001718 <__udivsi3> ; раз udiv
__aeabi_uidivmod:
0x20001824 cmp r1, #0
0x20001826 beq.n 0x20001818 <__udivsi3+256>
0x20001828 b.n 0x20001718 <__udivsi3> ; два udiv
0x2000182a bx lr- COKPOWEHEU
- Говорящий с текстолитом
- Сообщения: 1525
- Зарегистрирован: Чт июн 10, 2010 20:11:19
Re: Лучше не использовать типы char в stm32?
Да? И где там исходный вариант jcxz - который с переворотом строки?Под спойлером есть все, что нужно
Даже не знаю что еще считать, если я уже даже название этой функции выложил. Ну ладно. Вот для AVR, с которого мы начали обсуждение:Ну давай считать:
Спойлер
Код: Выделить всё
char* u32toa(uint32_t val, char *buf){
c0: cf 92 push r12
c2: df 92 push r13
c4: ef 92 push r14
c6: ff 92 push r15
c8: cf 93 push r28
ca: df 93 push r29
cc: dc 01 movw r26, r24
ce: cb 01 movw r24, r22
buf+=10;
d0: ea 01 movw r28, r20
d2: 2a 96 adiw r28, 0x0a ; 10
buf[1] = 0;
d4: fa 01 movw r30, r20
d6: 13 86 std Z+11, r1 ; 0x0b
do{
buf[0] = val % 10;
d8: 2a e0 ldi r18, 0x0A ; 10
da: c2 2e mov r12, r18
dc: d1 2c mov r13, r1
de: e1 2c mov r14, r1
e0: f1 2c mov r15, r1
val /= 10;
e2: bc 01 movw r22, r24
e4: cd 01 movw r24, r26
e6: a7 01 movw r20, r14
e8: 96 01 movw r18, r12
ea: 36 d0 rcall .+108 ; 0x158 <__udivmodsi4>
ec: da 01 movw r26, r20
ee: c9 01 movw r24, r18
if(buf[0] < 10)buf[0] += '0'; else buf[0] = buf[0] - 0x0A + 'A';
f0: 60 5d subi r22, 0xD0 ; 208
f2: 68 83 st Y, r22
buf--;
f4: 21 97 sbiw r28, 0x01 ; 1
}while(val);
f6: 23 2b or r18, r19
f8: 24 2b or r18, r20
fa: 25 2b or r18, r21
fc: 91 f7 brne .-28 ; 0xe2 <u32toa+0x22>
return buf;
}
fe: ce 01 movw r24, r28
100: df 91 pop r29
102: cf 91 pop r28
104: ff 90 pop r15
106: ef 90 pop r14
108: df 90 pop r13
10a: cf 90 pop r12
10c: 08 95 ret
Вот для arm-m3:
Спойлер
Код: Выделить всё
08000534 <u32toa>:
char* u32toa(uint32_t val, char *buf){
buf+=11;
buf[0] = 0;
8000534: 2200 movs r2, #0
char* u32toa(uint32_t val, char *buf){
8000536: b510 push {r4, lr}
do{
buf--;
buf[0] = val % 10;
val /= 10;
8000538: 240a movs r4, #10
char* u32toa(uint32_t val, char *buf){
800053a: 4603 mov r3, r0
buf[0] = 0;
800053c: 72ca strb r2, [r1, #11]
buf+=11;
800053e: f101 000b add.w r0, r1, #11
val /= 10;
8000542: fbb3 f1f4 udiv r1, r3, r4
buf[0] = val % 10;
8000546: fb04 3211 mls r2, r4, r1, r3
if(buf[0] < 10)buf[0] += '0'; else buf[0] = buf[0] - 0x0A + 'A';
}while(val);
800054a: 2b09 cmp r3, #9
if(buf[0] < 10)buf[0] += '0'; else buf[0] = buf[0] - 0x0A + 'A';
800054c: f102 0230 add.w r2, r2, #48 ; 0x30
8000550: f800 2d01 strb.w r2, [r0, #-1]!
}while(val);
8000554: d800 bhi.n 8000558 <u32toa+0x24>
return buf;
}
8000556: bd10 pop {r4, pc}
val /= 10;
8000558: 460b mov r3, r1
800055a: e7f2 b.n 8000542 <u32toa+0xe>
---
Такое ощущение, что вы упорно пытаетесь съехать с исходной темы на ARM-M0. Но чем именно эта архитектура примечательна? Вроде как никто на ней изначально акцента не делал.
Re: Лучше не использовать типы char в stm32?
COKPOWEHEU, я писал, что для M3 и выше будет замена на умножение, хотя там и деление относительно быстрое, а для M0 будет софтовое деление и потому твой подход будет одним из самых медленных. И после этого ты попросил пруфы, которые с первого раза вообще не заметил, со второго видимо заметил только половину, а на третий раз в ход пошли тесты для AVR, а я оказался виновным в съезжании с AVR на M0
Какое отношение к медленной работе функции на M0, а я утверждал именно это, имеют реализации для M3 или AVR? И переворот строки тут не при чем, потому что речь о двойном и медленном делении, переворачивая строку можно дополнительно затормозить что угодно и вообще ни у меня, ни у тебя переворота строки нет, чего мне обсуждать недостатки реализации третьего лица? Читать нужно внимательнее, а то под спойлеры не заглядываем и независимо от того, что пишут другие считаем, что речь идет об AVR 
- COKPOWEHEU
- Говорящий с текстолитом
- Сообщения: 1525
- Зарегистрирован: Чт июн 10, 2010 20:11:19
Re: Лучше не использовать типы char в stm32?
[uquote="Reflector",url="/forum/viewtopic.php?p=4109955#p4109955"]Читать нужно внимательнее, а то под спойлеры не заглядываем и независимо от того, что пишут другие считаем, что речь идет об AVR
[/uquote]
При чем здесь спойлеры? Вот прямая цитата:
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4106002#p4106002"]Кстати, мне после AVR не нравится сдвиг на (4*j), лучше бы исходное число двигать, как в выводе десятичного:[/uquote]
А вот про М0 как раз речь не шла.
---
Впрочем, теперь это уже чисто академический вопрос. Измерения я провел, результаты выложил. Вариант с делением ненамного медленнее сдвигов, зато читабельнее.
При чем здесь спойлеры? Вот прямая цитата:
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4106002#p4106002"]Кстати, мне после AVR не нравится сдвиг на (4*j), лучше бы исходное число двигать, как в выводе десятичного:[/uquote]
А вот про М0 как раз речь не шла.
При чем здесь M0? На AVR, где нет не только аппаратного умножения и деления, но хоть какой-то работы с числами больше 8 бит, разница незначительная.Какое отношение к медленной работе функции на M0
Посмотрите вот на это сообщение, вот на это. И потом на свое. И сравните где результаты измерений, а где куски кода.И после этого ты попросил пруфы, которые с первого раза вообще не заметил
---
Впрочем, теперь это уже чисто академический вопрос. Измерения я провел, результаты выложил. Вариант с делением ненамного медленнее сдвигов, зато читабельнее.
Re: Лучше не использовать типы char в stm32?
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4109993#p4109993"]При чем здесь M0? На AVR, где нет не только аппаратного умножения и деления, но хоть какой-то работы с числами больше 8 бит, разница незначительная.[/uquote]
M0 при том, что про AVR я даже не заикался, первый мой пост касался M3(и выше) и M0, а в качестве проблемной серии я обозначил только последнюю. То что ты до того обсуждал AVR с другими людьми, хотя они тоже задавались вопросом при чем тут AVR, меня совершенно не каcается. Если говоря про M0, при этом цитируя пост про L151, т.е. тоже про STM32, от меня требуют тестов, то это тесты для M0, думать что они для AVR нет никаких оснований.
M0 при том, что про AVR я даже не заикался, первый мой пост касался M3(и выше) и M0, а в качестве проблемной серии я обозначил только последнюю. То что ты до того обсуждал AVR с другими людьми, хотя они тоже задавались вопросом при чем тут AVR, меня совершенно не каcается. Если говоря про M0, при этом цитируя пост про L151, т.е. тоже про STM32, от меня требуют тестов, то это тесты для M0, думать что они для AVR нет никаких оснований.
Ладно, по первому линку можно прочитать что две вариации тестируемого кода выполняются за 10180 и 11116 тактов, т.е. первый вариант немного быстрее, но разве что-то принципиально поменяется если этих тактов будет 15000 и 16000? Нет, по-прежнему первый вариант будет немного быстрее, потому у меня было написано, что последняя функция, у которой убрано вычисление остатка от деления и заменено само деление выполняется в 2.7 раза быстрее оригинальной функции у которой это все есть. Какие функции, как тестировалось и сколько тактов получилось - это все под спойлером, для особо любознательных.
- COKPOWEHEU
- Говорящий с текстолитом
- Сообщения: 1525
- Зарегистрирован: Чт июн 10, 2010 20:11:19
Re: Лучше не использовать типы char в stm32?
Разве L151 это М0? Да нет, сейчас перепроверил: все stm'ки, на которых я могу протестировать - F100, F103, L151 - относятся к М3.Если говоря про M0, при этом цитируя пост про L151
А в 2.7 раза это сколько в тактах? Может, там как и в AVR десять тысяч тактов и оба варианта годятся только отладочную информацию раз в вечность передавать. То есть не "данные для любознательных" закопаны под спойлер, а просто какие-то сырые данные без пояснений.было написано, что последняя функция, у которой убрано вычисление остатка от деления и заменено само деление выполняется в 2.7 раза быстрее