В этот последний день перед Рождеством я решил взглянуть на производительность вычислений с плавающей запятой на 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