Ускоряем деление на ESP32, ESP32-S3 и ESP32-S2

Вариант деления через умножение (a / b == a * (1/b)) + две итерации Ньютона-Рафсона :). (источник не помню, откуда-то с гитхаба).

Проверил прирост скорости очень грубо - 1.67 раза на делении в цикле (накладные на цикл не учтены, надо будет развернуть цикл, перепроверить, думаю, что там >2 раз gain).

Работает и в Си м в Си++.

Использовать так: float a = DIV(b, c)


__attribute__((always_inline)) inline float recipsf2(float input) {

    float result, temp;

    asm(
        "recip0.s %0, %2\n"
        "const.s %1, 1\n"
        "msub.s %1, %2, %0\n"
        "madd.s %0, %0, %1\n"
        "const.s %1, 1\n"
        "msub.s %1, %2, %0\n"
        "maddn.s %0, %0, %1\n"
        :"=&f"(result),"=&f"(temp):"f"(input)
    );
    return result;
}

#define DIV(a, b) (a)*recipsf2(b)


Прирост скорости так же обеспечивается тем, что параметры передаются сразу в регистрах FPU: по какой-то причине компилятор GCC для Xtensa передает float в регистрах общего назначения, а потом перепаковывает в float регистры. Такой подход снижает возможности по оптимизации самим компилятором, поэтому - кладем переменные сразу в F регистры, высвобождая 3 обычных регистра.

Точность будет ниже, но приемлемо.

Это слово не имеет смысла без уточнения задачи. Для какой-то задачи приемлемо, для какой-то – нет.

Ну не для точных расчетов. Для точных берите double.

22..23 бита мантиссы в зависимости от входных данных. Точность около 1 ULP. На маленьких числах : 10 в -7 степени, на больших - 10 в минус пятой. По порядку величины.

Первая инструкция вычисляет 1/x и 8 бит мантиссы. Потом две итерации, чтобы поправить грубый результат. Каждая итерация примерно удваивает количество бит мантиссы. В смысле точности :).

Я в экспериментах не заметил разницы - получал одинаковые ответы, все знаки после точки совпадали. Числа делил маленькие на большие.