Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

Re: Лучше не использовать типы char в stm32?

Сб окт 23, 2021 22:55:48

Принудительная вакцинация вторым умножением, максимальный изврат: 40 байт на функцию, 7 тактов на цикл.
https://godbolt.org/z/c1MYarv43
Без оптимизации -Os - второе умножение не появляется.
Код:
__attribute__ ((optimize("-Os"))) char* u32_char (uint32_t value, char* tail_txt) /// 40 байт
{
    *tail_txt = 0;
    uint32_t tmp, exx;
    do{
        tmp = ((uint64_t) value * 429496730UL >> 32);
        exx = value - tmp * 10;
        exx += '0';
        *(--tail_txt) = exx;
        value = tmp;
    }while (value != 0);
    return tail_txt;
};

Re: Лучше не использовать типы char в stm32?

Сб окт 23, 2021 23:03:28

Разве L151 это М0? Да нет, сейчас перепроверил: все stm'ки, на которых я могу протестировать - F100, F103, L151 - относятся к М3.

Изначально Eddy_Em просил потестить на F0(M0), у тебя таких нет, потому появился тест [только] для L151(M3), я же написал, что на M0 функция с делением и взятием остатка будет медленно работать и на требование предоставить доказательства потестил на G0(M0+). Нигде не звучало, что L151 - это M0, сказано было, что L151 - это тоже STM32. Все трое обсуждали STM32, двое конкретно говорили про ядро M0 и все было хорошо пока не оказалось, что тесты оказывается нужны для AVR. Не буду говорить за других, но лично я не настолько прозорливый :)


COKPOWEHEU писал(а):А в 2.7 раза это сколько в тактах? Может, там как и в AVR десять тысяч тактов и оба варианта годятся только отладочную информацию раз в вечность передавать. То есть не "данные для любознательных" закопаны под спойлер, а просто какие-то сырые данные без пояснений.

Спор был про скорость, а не нужность, числа на печать выводили и на 8051 исполняющих инструкции за 12 тактов, что уж говорить про 32-х битные мк c 32-х битным умножением разгоняющиеся до 140MHz. И вообще информация про 2.7 раза уже устарела, новый рекорд для M0 - 6.2 раза вот с такой функцией деления:
Код:
static int remu10(uint32_t n)
{
   const static int8_t table[16] = { 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 8, 9, 0 };
   n = (0x19999999 * n + (n >> 1) + (n >> 3)) >> 28;
   return table[n];
}

static uint32_t divu10(uint32_t n)
{
   return ((n - remu10(n)) >> 1) * 0xCCCCCCCD;
}

Re: Лучше не использовать типы char в stm32?

Сб окт 23, 2021 23:32:59

А вот на stm32L151 не удержался и проверил.

Проверяй ещё раз, твоё умножение требует сдвига на 35, ну или на 3 в старшем регистре. Хотя можно умножать на число не требующее дополнительной обработки результата.
И я его нашёл 429496730UL.

Re: Лучше не использовать типы char в stm32?

Сб окт 23, 2021 23:52:10

AVI-crak, gcc не зря когда заменяет деление на 10 сдвигает на 35 бит, простейший тест на пк обнаруживает тьму ошибок:
Код:
uint32_t divu10(uint32_t val) { return (uint64_t)val * 429496730UL >> 32; }

for(uint64_t i = 0; i <= 0xFFFF'FFFF; i++)
{
   if(divu10(i) != i / 10)
   {
        printf("Error: %u, %u, %u\n", i, divu10(i), i / 10);
        return 0;
   }
}
printf("OK!\n");

Первая из них такая: Error: 1073741829, 107374183, 107374182
Последний раз редактировалось Reflector Сб окт 23, 2021 23:56:38, всего редактировалось 1 раз.

Re: Лучше не использовать типы char в stm32?

Сб окт 23, 2021 23:56:15

А если расфигачить STM32 на uint128_t? ☺
Да не, чего ходить вокруг да около? Давайте операции через uint256_t делать!!!
Вай, да: на современных процах с AVX2 и uint512_t реализовать не проблема. Давайте ими насиловать 32-битные МК!

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 00:03:45

Вай, да: на современных процах с AVX2 и uint512_t реализовать не проблема. Давайте ими насиловать 32-битные МК!

На новых Cortex-M55 есть MVE - это как раз векторная фигня типа SSE, восемь 128-ми битных регистров :)

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 00:34:49

Reflector, подозреваю, что уж такие навороченные, но таки "M" никому нафиг не нужны будут! Уж дешевле тогда "A" взять, поставить нормальный линукс, да работать! А в качестве рилтайма по-старинке использовать те же самые M0 или M3.

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 08:08:00

подозреваю, что уж такие навороченные, но таки "M" никому нафиг не нужны будут!

У M55 просто этот MVE есть, ну и ядро новое Armv8-M с небольшим количеством новых инструкций, а в остальном M7 так самым производительным и остался и до подорожания китайцы 480MHz H750 по $4-5 продавали и его очень даже брали, я, например :)

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 09:48:29

Проверяй ещё раз, твоё умножение требует сдвига на 35, ну или на 3 в старшем регистре

Несколько раз перечитал сообщение, но так и не понял что вы хотите. Что именно проверять, о каком умножении речь?
Изначально Eddy_Em просил потестить на F0(M0), у тебя таких нет, потому появился тест [только] для L151(M3)
Нет, тест появился не из-за EddyEm, тест появился, поскольку я подобными функциями пользуюсь, и было интересно узнать насколько они лучше или хуже альтернатив.
Спор был про скорость
Так для вас это всего лишь спор? Соревнование на самый быстрый код? Меня-то вопрос интересует в практическом смысле: стоит ли использовать и для каких задач.

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 11:09:39

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

Появились тесты из-за Eddy_Em, не из-за Eddy_Em или частично из него - это в принципе недоказуемо и уже только поэтому не существенно. Я описывал последовательность событий когда шло обсуждения тестов для STM32 и подключившись к обсуждению я говорил о них же, делая акцент на том, что на M0, которые интересуют Eddy_Em, функция с делением и взятием остатка будет работать медленно, что в последствии и было убедительно доказано. Ни о каких AVR в той череде постов не говорилось и, соответственно, ни к каким постоянным попыткам извернуться и съехать с AVR на M0 я не причастен :) И да, если бы мне написали, что моя функция работает в 2.7 раза медленнее, то я бы определенно разобрался в чем там дело, а когда если спойлер и открыл, то сразу его и закрыл, то можно говорить что угодно, но степень заинтересованности налицо :)

COKPOWEHEU писал(а):Так для вас это всего лишь спор? Соревнование на самый быстрый код? Меня-то вопрос интересует в практическом смысле: стоит ли использовать и для каких задач.

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

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 13:02:37

Ну вот я глянул, что производительность деления uint32_t и умножений/сдвигов uint64_t не очень-то сильно отличается.
Да, если нужно будет многократно делить на константы, можно попробовать оптимизировать через умножения и сдвиги. А в отдельных случаях, наверное, можно и не волноваться.
Еще есть деления вроде как на константу, но она берется в рантайме, а не вычисляется компилятором. Скажем, вычисление температуры:
Код:
int32_t getMCUtemp(){ // return MCU temperature (degrees of celsius * 10)
    int32_t ADval = getADCval(2);
    int32_t temperature = (int32_t) *TEMP30_CAL_ADDR - ADval;
    temperature *= (int32_t)(1100 - 300);
    temperature /= (int32_t)(*TEMP30_CAL_ADDR - *TEMP110_CAL_ADDR);
    temperature += 300;
    return(temperature);
}

А в вычислении Vdd наоборот: константа делится на изменяющееся число:
Код:
uint32_t getVdd(){ // return Vdd * 100 (V)
    uint32_t vdd = ((uint32_t) *VREFINT_CAL_ADDR) * (uint32_t)330; // 3.3V
    vdd /= getADCval(3);
    return vdd;
}

А в случае, если я захочу перевести ADU в Вольты, придется минимум два деления использовать.

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 13:27:30

AVI-crak, gcc не зря когда заменяет деление на 10 сдвигает на 35 бит, простейший тест на пк обнаруживает тьму ошибок:

Ок, животина оказалась бракованной, пришлось пристрелить. Хотя выглядело очень красиво.
Вывод из всего этого безобразия - нужно писать максимально просто.

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 13:34:43

Ну вот я глянул, что производительность деления uint32_t и умножений/сдвигов uint64_t не очень-то сильно отличается.

Ну если в 12 раз - это не "очень-то", то наверное. Cortex-M4:
Изображение
Так же как тут некоторые и между ARM и AVR разницы не видят, также для кого-то "в 12 раз" - это "не сильно отличаются".
Последний раз редактировалось jcxz Вс окт 24, 2021 13:40:45, всего редактировалось 1 раз.

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 13:48:11

Ну вот я глянул, что производительность деления uint32_t и умножений/сдвигов uint64_t не очень-то сильно отличается.

В целом да, но если взять мою последнюю функцию деления на 10, которая табличная и потому можно обойтись без 64 бит, то она на M0 отрабатывает всегда за 29 тактов, в то время как обычное деление медленнее и при небольших числах, а если делить максимальное 32-х битное значение, то уже будет 225 тактов...

Добавлено after 9 minutes 16 seconds:
Ну если в 12 раз - это не "очень-то", то наверное. Cortex-M4:

Для M4 и напрягаться особо не нужно, компилятор обычно сам все делает, а M0 не только при делении на константы косячит, помню всякие аналоги CLZ там тоже были в разы медленнее самописных.

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 14:01:34

Ну если в 12 раз - это не "очень-то"

Я верю своим глазам, а не рекламациям! А глаза мне показали в тесте Reflector разницу всего в полтора раза между делением uint32_t и умножением/сдвигами uint64_t.
И при чем здесь M4, когда мы обсуждаем M0, у которого нет аппаратного деления?
Что до М4, то приведенная таблица наглядно показывает: нет смысла на таком крутом ядре городить всякие умножения/сдвиги, когда реализация деления в среднем займет всего лишь 7 тактов! А на перемещение данных между регистрами, умножения и сдвиги уйдет не меньше!

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 14:23:33

Я верю своим глазам, а не рекламациям! А глаза мне показали в тесте Reflector разницу всего в полтора раза между делением uint32_t и умножением/сдвигами uint64_t.
Тесты на реальном железе подтверждают данные таблицы. Достаточно запустить отладчик и посмотреть. IAR, CM4: Изображение
Смотрим на строчку "CCSTEP".

И при чем здесь M4, когда мы обсуждаем M0, у которого нет аппаратного деления?
Если аппаратного деления нет, то разница будет ещё больше, кэп. Уже видимо - во много десятков раз. Или ещё больше.

нет смысла на таком крутом ядре городить всякие умножения/сдвиги, когда реализация деления в среднем займет всего лишь 7 тактов!
Всегда имеет смысл писать ПО, работающее быстрее. Даже если разница всего в несколько %. А когда разница в 7 раз(!), то тут даже вопроса не возникает - это должно быть очевидно.

PS: Да, и в реальных программах, работающих в реал-тайм, смотрят не на "среднее по больнице", а на худший случай (максимальное время выполнения). Т.е. - не 7 тактов, а 12 тактов.

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 14:33:23

Если аппаратного деления нет, то разница будет ещё больше, кэп. Уже видимо - во много десятков раз. Или ещё больше.

На M0 не только деления нет, но и длинного умножения, потому для деления на 10 через умножение получаем порядка 60-ти инструкций и все они будут выполняться даже если нужно 10 на 10 поделить.

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 15:33:19

И да, если бы мне написали, что моя функция работает в 2.7 раза медленнее, то

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

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 15:45:10

Так, вытащил из закромов функцию длинного умножения для M0 на замену той жести что генерит gcc и divu10() стала отрабатывать за 27 тактов:

Re: Лучше не использовать типы char в stm32?

Вс окт 24, 2021 16:11:06

На M0 не только деления нет, но и длинного умножения, потому для деления на 10 через умножение получаем порядка 60-ти инструкций и все они будут выполняться даже если нужно 10 на 10 поделить.
Уверены? Что-то многовато как-то...
Проверил (в наличии у меня M0 нет), поэтому под симулятором. Написал такую функцию:
Код:
               PUBLIC   umull
umull:         MOV      R12, R4
               LSRS     R2, R0, #16
               LSRS     R3, R1, #16
               UXTH     R0, R0
               UXTH     R1, R1
               MOV      R4, R2
               MULS     R4, R4, R3
               MULS     R2, R2, R1
               MULS     R3, R3, R0
               MULS     R0, R0, R1
               LSRS     R0, R0, #16
               MOVS     R1, #0
               ADDS     R2, R2, R3
               MOV      R3, R1
               ADCS     R3, R3, R1
               ADDS     R2, R2, R0
               ADCS     R3, R3, R1
               LSRS     R2, R2, #16
               ADDS     R4, R4, R2
               LSLS     R3, R3, #16
               ADDS     R0, R3, R4
               MOV      R4, R12
               BX       LR
Если где-то не напутал, вроде считает правильно.
Вызываем:
Код:
  umull(0x1234567u, 0x23456789u);

Всё вместе (вместе с вызовом BL и возвратом) по данным симулятора выполняется за 24 такта. Это далеко не 60....
Ответить