Логика очень простая - новые технологии экономят флеш, и значительно повышают быстродействие.
А какие из технологий здесь вы считаете новыми? Если оптимизатор компилятора, то он одинаковый в обоих случаях. А использованные алгоритмы преобразования в строку не менялись с каких-то допотопных времен.
Пока что единственное, что нам известно - что мой код в занимает в 1.6 раз меньше места в ПЗУ. Поскольку никаких кэширований и других оптимизаций на скорость не использовалось, логично предположить, что он будет быстрее выполняться.
Дарю самый быстрый в мире код. Одна ассемблерная команда будет всего.
Все-таки проверил скорость работы. Правда, под рукой оказался только древний AVRStudio, у которого далеко не лучшая оптимизация. Ну да для грубой оценки сойдет. Вариант jcxz выполняется за 10180 тактов. Вариант мой - за 11116 Результат: вариант jcxz чуть быстрее. Для моих задач (обычно это отладочный вывод на UART или дисплей с человеко-читаемой скоростью) эта разница не критична, и я скорее всего продолжу пользоваться своим вариантом, как более читаемым. Еще, наверное, проверю на ARM-M3, поскольку такие камни у меня есть, и поскольку аппаратной математики у них побольше, чем у AVR, с которых мы начинали.
COKPOWEHEU, а как насчет на F0 проверить? Что-то меня гложут сомнения, что операции над uint64_t будут выполняться быстрей, чем деление (пусть и софтовое) на 10 uint32_t.
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей.
Мне неначем: ни симулятора, ни железки нет. --- А вот на 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 }
На конвертацию числа 4294967295 уходит примерно 200 тактов. Исправленный мой вариант: Спойлер
Выполняется примерно за 176 тактов Гибридный вариант: Спойлер
Код:
#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 }
Выполняется за 137 тактов. Результат: исходный вариант jcxz выполняется уже чуть дольше моего, скорее всего из-за того, что приходится в конце переворачивать строку. Если заполнять массив справа налево, выполняется уже быстрее.
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
На M3 и выше, как я уже говорил, компилятор сам заменяет деление умножением, при том что там есть и аппаратное деление с которым результат будет не особо хуже, а вот на М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);
Последний вариант быстрее в 2.7 раза, хотя разница падает по мере уменьшения передаваемого значения, т.к. софтовое деление тогда завершается быстрее. Если числа порядка 1000, то первый и последний вариант выдают сравнимые значения, в среднем наверное второй вариант самый оптимальный, тем более так можно не только десятичные числа выводить.
Reflector, а зачем там кусок от вывода шестнадцатеричных остался? `else *buf -= 0x0A + 'A';` не будет выполняться. А выводить шеснадцатеричные числа значительно проще, там никаких делений не нужно:
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Напомню что язык Си совершенно бесплатно даёт возможность отстрелить себе все конечности. У нас именно такой случай. В идеале нужно обвесить функцию проверками размера стека,
Разверните свою мысль чтобы понятна была. Какой случай? При чем здесь стек? Через стек передаются только uint32_t в качестве аргумента и char* (обычно uint16_t либо uint32_t) в качестве буфера. Уж 6-8 свободных байт в стеке скорее всего найдется.
Цитата:
Берем STM32G0(M0+) и тестим(-O2)
Да, именно это мне и нужно, что ж вы изворачиваетесь-то постоянно от прямого вопроса! Где тесты? Опять от вас одни "логические соображения". В общем, пока пруфов нет, остается пользоваться моими данными: деление через B35 чуть-чуть быстрее, но менее читаемое. И, естественно, буфер надо заполнять с конца.
Да, именно это мне и нужно, что ж вы изворачиваетесь-то постоянно от прямого вопроса! Где тесты? Опять от вас одни "логические соображения".
Смотри, есть такой код:
Код:
buf[0] = val % 10; val /= 10;
Если посмотреть что генерит gcc для M0, то там будут вызовы функций __aeabi_uidivmod() и __udivsi3(), при это первая дополнительно вызывает вторую, т.е. деление выполняется дважды о чем тебе уже говорил jcxz и я об этом также знал еще когда писал свои функции конвертации, так что это никакие не чисто "логические соображения". Более того я написал три разных функции, протестил их и результаты спрятал под спойлер, но видимо спрятал слишком хорошо, раз не все смогли с ними ознакомиться
я написал три разных функции, протестил их и результаты
Три разных функции это хорошо, жаль вы не выложили по ним результаты. Там под спойлером есть какие-то чиселки, но без контекста они никакого смысла не несут.
Цитата:
т.е. деление выполняется дважды
Деление выполняется единажды, о чем я уже писал. Получение частного и остатка это одна функция.
Три разных функции это хорошо, жаль вы не выложили по ним результаты. Там под спойлером есть какие-то чиселки, но без контекста они никакого смысла не несут.
Под спойлером есть все, что нужно, разве что класс замеряющий время выполнения придется чем-то заменить, если хочется мерять самому. Я даже на всякий случай проверил насколько точно измеряет и для nop<500>(), т.е. 500 подставленных NOP получил ровно 500 тиков.
COKPOWEHEU писал(а):
Деление выполняется единажды, о чем я уже писал. Получение частного и остатка это одна функция.
Инструкции udiv и mls вижу, подпрограмм деления с ходу не увидел. --- Такое ощущение, что вы упорно пытаетесь съехать с исходной темы на ARM-M0. Но чем именно эта архитектура примечательна? Вроде как никто на ней изначально акцента не делал.
COKPOWEHEU, я писал, что для M3 и выше будет замена на умножение, хотя там и деление относительно быстрое, а для M0 будет софтовое деление и потому твой подход будет одним из самых медленных. И после этого ты попросил пруфы, которые с первого раза вообще не заметил, со второго видимо заметил только половину, а на третий раз в ход пошли тесты для AVR, а я оказался виновным в съезжании с AVR на M0 Какое отношение к медленной работе функции на M0, а я утверждал именно это, имеют реализации для M3 или AVR? И переворот строки тут не при чем, потому что речь о двойном и медленном делении, переворачивая строку можно дополнительно затормозить что угодно и вообще ни у меня, ни у тебя переворота строки нет, чего мне обсуждать недостатки реализации третьего лица? Читать нужно внимательнее, а то под спойлеры не заглядываем и независимо от того, что пишут другие считаем, что речь идет об AVR
Кстати, мне после AVR не нравится сдвиг на (4*j), лучше бы исходное число двигать, как в выводе десятичного:
А вот про М0 как раз речь не шла.
Цитата:
Какое отношение к медленной работе функции на M0
При чем здесь M0? На AVR, где нет не только аппаратного умножения и деления, но хоть какой-то работы с числами больше 8 бит, разница незначительная.
Цитата:
И после этого ты попросил пруфы, которые с первого раза вообще не заметил
Посмотрите вот на это сообщение, вот на это. И потом на свое. И сравните где результаты измерений, а где куски кода. --- Впрочем, теперь это уже чисто академический вопрос. Измерения я провел, результаты выложил. Вариант с делением ненамного медленнее сдвигов, зато читабельнее.
При чем здесь M0? На AVR, где нет не только аппаратного умножения и деления, но хоть какой-то работы с числами больше 8 бит, разница незначительная.
M0 при том, что про AVR я даже не заикался, первый мой пост касался M3(и выше) и M0, а в качестве проблемной серии я обозначил только последнюю. То что ты до того обсуждал AVR с другими людьми, хотя они тоже задавались вопросом при чем тут AVR, меня совершенно не каcается. Если говоря про M0, при этом цитируя пост про L151, т.е. тоже про STM32, от меня требуют тестов, то это тесты для M0, думать что они для AVR нет никаких оснований.
COKPOWEHEU писал(а):
Посмотрите вот на это сообщение, вот на это. И потом на свое. И сравните где результаты измерений, а где куски кода.
Ладно, по первому линку можно прочитать что две вариации тестируемого кода выполняются за 10180 и 11116 тактов, т.е. первый вариант немного быстрее, но разве что-то принципиально поменяется если этих тактов будет 15000 и 16000? Нет, по-прежнему первый вариант будет немного быстрее, потому у меня было написано, что последняя функция, у которой убрано вычисление остатка от деления и заменено само деление выполняется в 2.7 раза быстрее оригинальной функции у которой это все есть. Какие функции, как тестировалось и сколько тактов получилось - это все под спойлером, для особо любознательных.
Если говоря про M0, при этом цитируя пост про L151
Разве L151 это М0? Да нет, сейчас перепроверил: все stm'ки, на которых я могу протестировать - F100, F103, L151 - относятся к М3.
Цитата:
было написано, что последняя функция, у которой убрано вычисление остатка от деления и заменено само деление выполняется в 2.7 раза быстрее
А в 2.7 раза это сколько в тактах? Может, там как и в AVR десять тысяч тактов и оба варианта годятся только отладочную информацию раз в вечность передавать. То есть не "данные для любознательных" закопаны под спойлер, а просто какие-то сырые данные без пояснений.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 12
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения