В этот последний день перед Рождеством я решил взглянуть на производительность вычислений с плавающей запятой на ESP32. Согласно паспорту, ​​процессор должен содержать аппаратный блок с плавающей запятой:

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

Для справки, я провел те же тесты на STM32F767, процессоре ARM Cortex M7 с аппаратной поддержкой операций с плавающей запятой двойной точности.

Чтобы измерить время, я подключил выход GPIO процессора к осциллографу и установил для GPIO значение HIGH непосредственно перед, а затем снова значение LOW после выполнения операции с плавающей запятой:

Базовый набор тестов состоит из следующих операций:

float f1 = 1.11111111111111111111;
float f2 = 4.44444444444444444444;
float fSum = f1 + f2;
float fSqr = sqrtf(f1);
float fMul = f1 * f2;
float fDiv = f1 / f2;
float fPow = powf(f1, f2);
float fSin = sinf(f1);
double d1 = 1.11111111111111111111;
double d2 = 4.44444444444444444444;
double dSum = d1 + d2;
double dSqr = sqrt(d1);
double dMul = d1 * d2;
double dDiv = d1 / d2;
double dPow = pow(d1, d2);
double dSin = sin(d1);

На моем Macbook Pro с GCC эти вычисления приводят к следующим значениям:

--------- float  ---------------------------------------
f1   = 1.111111164093017578, f2   = 4.444444656372070312
fSum = 5.555555820465087891, fSqr = 1.054092526435852051
fMul = 4.938271999359130859, fDiv = 0.250000000000000000
fPow = 1.597227334976196289, fSin = 0.896192252635955811
--------- double ---------------------------------------
d1   = 1.111111111111111160, d2   = 4.444444444444444642
dSum = 5.555555555555555358, dSqr = 1.054092553389459841
dMul = 4.938271604938272219, dDiv = 0.250000000000000000
dPow = 1.597226932648388109, dSin = 0.896192201029956337

Я отметил, что они могут проверить, действительно ли процессоры используют двойную точность для типа «double», и вернуть разумные результаты.

Начнем с эталонных результатов по STM32F767. Результаты представлены в микросекундах, синий - одинарной, оранжевый - двойной точности, скомпилирован с отключенной оптимизацией (-O0):

Во-первых, хорошие новости: все результаты были правильными (или, по крайней мере, такими же, как на MacBook Pro… :-)

Сложение, умножение, а также деление выполняются невероятно быстро (от 100 до 200 нс, даже если они включают присвоение переменной результата и установку / очистку GPIO).

Для функций pow и sin 64-битный вариант лишь немного медленнее, чем 32-битный вариант, тогда как извлечение квадратного корня занимает более чем в 6 раз больше времени с более высокой точностью.

Итак, как ESP32 сравнивается с этими результатами?

Я загрузил новую установку ESP-IDF от 23.12.2016 и использовал стандартную настройку (240 МГц, компиляция «Отладка»), время снова в микросекундах:

Результаты примерно на один-два порядка хуже, чем на STM32F767. На первый взгляд кажется, что расчеты происходят программно. Но взгляд на промежуточный ассемблерный код не подтверждает это предположение - по крайней мере, для сложения и умножения с одинарной точностью, похоже, используются специальные инструкции (add.s, mul.s):

Переключение GPIO не может быть причиной. Я быстро провел тест без операций с плавающей запятой между переключениями, и это заняло всего около 200 нс.

Как и ожидалось, результат для более сложного примера не намного лучше. Я сравнил производительность расчета местного видимого звездного времени по формулам из Астрономического альманаха и получил следующие результаты (время в микросекундах, чем короче, тем быстрее):

Синие полосы соответствуют «отладочной» сборке (-O0 для STM32F767), оранжевые полосы - «выпускной» сборке в инструменте настройки ESP32 (-O3 для STM32F767). Разница между включенной и отключенной оптимизацией незначительна.

В левой половине графика вы можете увидеть результаты для STM32F767, сначала с одинарной, затем с двойной точностью. Неожиданно то, что реализация с двойной точностью (с использованием типов данных «double» и функций «sin», «cos», «floor») примерно в 3 раза быстрее, чем реализация с одинарной точностью (с использованием типов данных «float» и Функции «sinf», «cosf», «floorf»). Перепроверила, не перепутала, но результат однозначный.

В правой половине - результаты для ESP32. Интересный эффект заключается в том, что измерение с двойной точностью (два крайних правых столбца) также немного быстрее, чем измерение с одинарной точностью, как в случае с STM32F767.

Я взглянул на промежуточный код ассемблера для STM32F767. Компиляция с использованием «float» привела к гораздо большему количеству кода (левая сторона на изображении ниже, по сравнению с «double» справа), особенно много из того, что, как мне кажется, является «конверсионным» кодом (vcvt.f32.f64, vcvt. f64.f32,). Но опять же, преобразования должны быть довольно быстрыми и никоим образом не замедлять вычисления в 4 раза:

Итак, в конце концов, я получил несколько неожиданных результатов, которые определенно требуют дополнительных экспериментов. Комментарии очень приветствуются :-).

Наслаждайтесь рождественскими каникулами!

Обновление, 23.12.2016: обсуждение на форуме Espressif: http://www.esp32.com/viewtopic.php?f=14&t=800