Про micros() или странная оптимизация
- Войдите на сайт для отправки комментариев
Доброй субботы, аксакалы!
Пока даже примерно не могу сообразить, растут ли ноги вопроса в аппаратных есебенностях ESP, особенностях оптимизации IDE или моей необразованности, поэтому - в песочнице.
Набросал вспомогательную функцию для формирования имен файлов для даталоггера на SD. Краткое содержание функции: получает адрес частично сформированного шаблона и вставляет в нужные места дату и id исполнительного устройства: 0ID_ММДД.ext
Для ускорения работы добавлены условные варианты. В легких случайх обходимся простым присваиванием сонстанты. В неочевидных - немного математики. То есть, например id = 5 (005) должен подставляться быстрее скажем 241.
В общем, дальше просто выложу листинг с результатами.
// ESP32 devkit v1 4mB
// v.1.8.13
// ESP core v 1.0.4
void setup() {
Serial.begin(115200);
Serial.println('\n');
Serial.println("Из setup: ");
char filename[13] = "***_****.day" ;
delay(1000);
Serial.print("fName, mks: ");
uint32_t mks = micros();
fName1(filename, 123, 12, 31);
Serial.println(micros() - mks);
Serial.println(filename);
Serial.print("fName, mks: ");
mks = micros();
fName1(filename, 1, 2, 3);
Serial.println(micros() - mks);
Serial.println(filename);
Serial.print("fName, mks: ");
mks = micros();
fName1(filename, 123, 12, 31);
Serial.println(micros() - mks);
Serial.println(filename);
Serial.println('\n');
Serial.println("Из функции: "); // Те же действия, перепакованные в функцию
bla(1, 1, 1);
bla(10, 1, 1);
bla(123, 1, 1);
bla(1, 10, 1);
bla(1, 1, 10);
bla(123, 12, 31);
}
void bla(uint8_t id, uint8_t m, uint8_t d){
char filename[13] = "***_****.day" ;
Serial.print("fName, mks: ");
uint32_t mks = micros();
fName1(filename, id, m, d);
Serial.println(micros() - mks);
Serial.println(filename);
}
/*
* char filename[13] = "0ID_MMDD.ext" (8.3);
* fName(filename,relay_id, mon, day, );
*/
void fName1(char * ch, uint8_t id, uint8_t mon, uint8_t day){ // 11-20 mks
if (id > 99) *ch = (char)(floor(id/100)+48); else *ch = '0';
if (id > 9) *(ch+1) = (char)(floor((id%100)/10)+48); else *(ch+1) = '0';
if (id > 9) *(ch+2) = (char)((id%10)+48); else *(ch+2) = (char)(id + 48);
if (mon>9)
{*(ch+4) = '1'; *(ch+5) = (char)(mon + 38);}
else {*(ch+4) = '0'; *(ch+5) = (char)(mon + 48);}
if (day>9)
{ uint8_t d1 = floor(day/10);
*(ch+6) = (char)(d1 + 48); *(ch+7) = (char)(day - (d1*10) + 48);}
else {*(ch+6) = '0'; *(ch+7) = (char)(day + 48);}
}
void loop() {}
Результат работы:
Собственно вопросы вызывают странные результаты micros():
Первый вызов функции fName() происходит существенно дольше всех последующих. Я даже там делэй(1000) добавил, чтобы ЕСПшка все свои стартовые дела успела доделать, хотя понимаю, что это фигня.
Следующие вызовы из setup хоть и подозрительно короткие, но логично отличаются по времени выполнения.
С вызовами из промежуточной функции вообще логики мало. Такое ощущение, что там микрос от балды лепится.
Подозрений несколько:
1. особенности реализации micros() при первом вызове.
2. Хитрый компилятор шельмует с повторным вызовом fName, хотя я не понимаю КАК.
Подскажите, куда рыть. Вопрос не принципиальный, просто хочу все знать))
А где вопрос то?
Блин, да Вы троллите нас!
Блин, да Вы троллите нас!
Евгений Петрович, и в мыслях не было. Вот туплю где-то жестко, это да. Вот только никак понять не могу где
А где вопрос то?
Вопрос простой. Почему такая разница в показаниях при выполнении одного кода. Точнее, вопрос в том, где я туплю
Вот только никак понять не могу где
floor - это функция для плавающей точки. Целочисленное деление И ТАК выполняется с отбрасыванием остатка. Вы же зачем-то вызываете floor для результата целочисленного деления, т.е. преобразуете аргумент к float, берёте целую часть, и преобразуете обратно к целому. При этом, при первом вызове, инициализируете библиотеку плавающей точки.
Но, это не всё. Чего Вы навертели в функции fName1? Нафига такие сложности-то? Показать Вам как она пишется?
Вот только никак понять не могу где
floor - это функция для плавающей точки. Целочисленное деление И ТАК выполняется с отбрасыванием остатка. Вы же зачем-то вызываете floor для результата целочисленного деления, т.е. преобразуете аргумент к float, берёте целую часть, и преобразуете обратно к целому. При этом, при первом вызове, инициализируете библиотеку плавающей точки.
Но, это не всё. Чего Вы навертели в функции fName1? Нафига такие сложности-то? Показать Вам как она пишется?
Про округление целочисленных вниз я если и знал, то забыл. В этом себе всю боль о организовал. За науку признателен. Все вопросы отпали. Теперь сам все сделаю, это же элементарно. Но Ваш пример хотел бы увидеть. Не для того, чтобы скопипастить, это не мой путь. Для знакомства с лучшими практиками
ЗЫ: пожалуй, выпью сегодня после баньки за ваше здоровье бокал эсторского))
Всё понимаю - но нахера - не понимаю.
А если электричество выключат? Или кошка-мышка хвостом махнёт?
Всё понимаю - но нахера - не понимаю.
А если электричество выключат? Или кошка-мышка хвостом махнёт?
Не в то окно, да? :)
Ну, вот, смотрите, Вы пишете(я убрал floor)
Но ведь, если id < 99, то частное от деления id/100 будет равен 0. Не проще ли написать
Да, этот вариант хуже тем, что деление выполняется всегда, а в Вашем случае только когда надо. Но такая экономия имела бы смысл, если бы это было в цикле с миллионом проходов. А так ... вы экономите копейки, зато усложняете программу (а усложнение - это удобрение почвы на которой баги растут).
Я бы сделал отдельную функцию, которая преобразует одно число, и вызывал бы её три раза. Примерно, так:
inline void convertNum(uint8_t num, char * dst, const bool from100 = false) { if (from100) { *dst++ = num / 100 + '0'; num = num % 100; } *dst++ = num / 10 + '0'; *dst = num % 10 + '0'; } void fName1(char * const ch, const uint8_t id, const uint8_t mon, const uint8_t day) { convertNum(id, ch, true); convertNum(mon, ch + 4); convertNum(day, ch + 6); }Ну, вот, смотрите, Вы пишете(я убрал floor)
Но ведь, если id < 99, то частное от деления id/100 будет равен 0. Не проще ли написать
Да, этот вариант хуже тем, что деление выполняется всегда, а в Вашем случае только когда надо. Но такая экономия имела бы смысл, если бы это было в цикле с миллионом проходов. А так ... вы экономите копейки, зато усложняете программу (а усложнение - это удобрение почвы на которой баги растут).
Вот не зря тему создал. Сейчас кажется, что это элементарное решение, но сам бы до этого в текущем году не дошел бы. После того, как функция стала укладываться в 1 мкс затратами процессорного времени на выполнение деления готов пренебречь)) Еще раз спасибо!