Использование «static» с «RTClib»

c++ arduino-due rtc performance

У меня вопрос относительно библиотеки Arduino "RTClib" от Adafruit и использования слова "статический".

Здесь вы можете увидеть отрывок из примера, предоставленного для часов реального времени pcf8523:

#include "RTClib.h"

RTC_PCF8523 rtc;

void setup () {

  while (!Serial) {
    delay(1);  // для Леонардо/Микро/Зеро
  }

  Serial.begin(57600);
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  if (! rtc.initialized()) {
    Serial.println("RTC is NOT running!");
  }
}

void loop () {
    DateTime now = rtc.now();

    Serial.print(now.year(), DEC);
    /*
    do some more stuff
    */
}

В начале loop() они написали:

DateTime now = rtc.now();

Таким образом, с каждой итерацией loop() "new" определяется заново (в куче?!), верно? Мне это кажется неэффективным.

Я думал переписать это так:

static DateTime now;    // статическое объявление, выполняется только один раз
now = rtc.now();    // присваивание «сейчас» каждый раз, когда loop() начинается заново

В этом случае "новое" должно размещаться в стеке как глобальная переменная, верно?

Я хочу использовать "статический" для повышения общей производительности за счет сокращения работы процессора и кучи (тем самым избегая фрагментации кучи). Имеет ли это смысл или вызовет больше проблем, чем решит? Имейте в виду, что я хочу изучить как правильное программирование для Arduino, так и правильное кодирование в целом.

Ссылка на библиотеку: https://github.com/adafruit/RTClib

Спасибо! :-)

EDIT: по какой-то причине я НЕ МОГУ объявить DateTime now; в глобальной области видимости, иначе программа не запустится. Хотя не знаю почему. Вот почему я хочу в первую очередь использовать static.

, 👍2

Обсуждение

Re «_Я НЕ МОГУ объявить DateTime now; в глобальной области видимости_»: что произойдет, если вы это сделаете? Ошибка компиляции? Неправильное поведение во время выполнения? Я попробовал, и он отлично компилируется (но у меня нет RTC для тестирования)., @Edgar Bonet

На самом деле, DateTime now = rtc.now() может использовать "copy elision", поэтому он конструируется непосредственно в переменную now и, следовательно, быстрее, чем ваш подход (поскольку вы фактически заблокировали эту возможность - поэтому он должен создать временный объект и скопируйте его в переменную now)., @KIIV

@EdgarBonet компилирует и загружает, но не выполняет Setup(). Setup() начинается с Serial.begin(9600); Serial.println("test"); Мигание встроенного светодиода тоже не работает., @blackdaw

@KIIV, что касается C++, я много раз читал, что отдельное объявление и определение могут быть частью «хорошей практики». Ваше объяснение никогда не упоминалось, но, кажется, имеет смысл! Может быть, это не имеет значения при (агрессивной) оптимизации компилятора?, @blackdaw

@blackdaw Я почти уверен, что речь идет об объявлении и определении методов класса, а не о переменных. И не правила/мифы: [Не настаивайте на том, чтобы все объявления были в верхней части функции](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#nr1-dont-insist-that -all-declarations-should-be-at-the-top-of-a-function) и два правила [Не вводите переменную (или константу) до того, как вам понадобится ее использовать](https://github.com /isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es21-не вводите-переменную-или-константу-перед-вам-необходимо-использовать-ее), @KIIV

@KIIV Вы правы. Я почерпнул много неправильных представлений из «руководств для начинающих». Тот, который вы связали, является очень полезным руководством, спасибо!, @blackdaw


2 ответа


Лучший ответ:

2

Вы писали:

DateTime now = rtc.now();

Таким образом, с каждой итерацией loop() "now" определяется заново (на куча?!), верно?

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

static DateTime now;    // статическое объявление, выполняется только один раз

В этом случае "новое" должно размещаться в стеке подобно глобальному переменная, верно?

Нет. Размещается статически, в разделе .bss, как неинициализированный глобальные переменные.

Я хочу использовать "статический" для повышения общей производительности за счет уменьшения работать на процессор

Не уверен, что это будет иметь большое значение. Статическое размещение может сэкономить вам несколько инструкций ЦП по сравнению с выделением стека (инструкции, которые переместить указатель стека). С другой стороны, это будет стоить вам нескольких инструкции по сравнению с распределением регистров (инструкции для доступа ОЗУ).

предотвращение фрагментации кучи

Распределение стека не использует кучу, и стек никогда не фрагментируется.

Имеет ли это смысл или вызовет больше проблем, чем решит?

Если вас действительно волнуют различия в микросекундах при выполнении время, надо либо смотреть и дизассемблировать программу, либо делать некоторый бенчмаркинг. В противном случае просто напишите то, что имеет больше смысла из точки зрения логики программы: нужно ли сохранять переменную его значение при вызовах loop()? Если ответ «нет», не используйте статический.

,

1

Вам не нужно использовать ключевое слово static.

Самое чистое решение — определить DateTime вне функции loop:

DateTime now;

Это выделит память и вызовет конструктор класса DateTime, инициализирующий ее.

Если вам нужна базовая переменная (например, int), вы должны инициализировать ее, например:

int now = 0;

Когда вы используете его в setup или loop, вы используете созданный экземпляр (без имени класса для его объявления):

void loop() {
   ...
   now = rtc.now();
}

Кроме того, найдите лучшее имя для now, потому что эта переменная совпадает с функцией rtc now.

Обновить

после вашего комментария его нельзя использовать как глобальную переменную:

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

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

RTC_PCF8523* rtc = NULL;

Чем вы создаете пустой указатель, не вызывая конструктор. Вы сделаете это в конце setup:

void setup() {
   ...
   rtc = new RTC_PCF8523();
}

Это приведет к тому, что переменная будет помещена в стек.

Чем при его использовании вы вызываете:

now = rtc->now();

Обратите внимание, что вы должны использовать нотацию ->, так как rtc — это указатель.

,

Забыл упомянуть: определение вне цикла() в глобальной области НЕ РАБОТАЕТ! Не спрашивайте меня, почему, я потратил часы, чтобы найти эту проблему. Это причина, по которой я хотел использовать статику в первую очередь. Отредактирую свой пост. А по поводу имени «сейчас»: это именно то, что они использовали в примере, я не буду его использовать, но хорошая мысль!, @blackdaw

Я обновил свой ответ, просто попробуйте, если это поможет., @Michel Keijzers

Что касается «_Я подозреваю, что конструктор вызывает функцию, которая использует переменные, или функцию, которая не инициализирована в этой точке_»: я не вижу ничего плохого в [этом конструкторе](https://github.com/adafruit/RTClib/ blob/1.4.1/RTClib.cpp#L157-L188)., @Edgar Bonet

@EdgarBonet ... я тоже, мне интересно, почему создание глобальной переменной не сработает., @Michel Keijzers

См. комментарии выше под решением @EdgarBonet. Он компилируется и загружается, но не выполняется., @blackdaw