Поделиться через


Основы программирования

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

Распространенные проблемы

Примечание

При создании пакетов образов для загрузки неопубликованных в рамках производственного процесса задайте AZURE_SPHERE_TARGET_API_SET соответствующую версию ОС Azure Sphere, с которой устройство было источником или восстановлено. В противном случае ОС Azure Sphere отклонит пакет образа.

  • Когда вы будете готовы развернуть приложение в рабочей среде, обязательно скомпилируйте окончательные пакеты образов в режиме выпуска.
  • Приложения часто развертываются в рабочей среде, несмотря на предупреждения компилятора. Применение политики нулевых предупреждений для полных сборок гарантирует, что каждое предупреждение компилятора будет намеренно устранено. Ниже приведены наиболее распространенные типы предупреждений, к которым настоятельно рекомендуется обращаться:
    • Неявные предупреждения, связанные с преобразованием: Ошибки часто возникают из-за неявных преобразований, возникающих в результате первоначальных быстрых реализаций, которые остались неустраченными. Например, код с множеством неявных числовых преобразований между различными числовыми типами может привести к потере критической точности или даже ошибкам вычисления или ветвления. Для правильной настройки всех числовых типов рекомендуется использовать как преднамеренный анализ, так и приведение, а не просто приведение.
    • Избегайте изменения ожидаемых типов параметров: Если при вызове API не выполняется явное приведение, неявные преобразования могут вызвать проблемы. например, переполнение буфера при использовании числового типа со знаком вместо числового типа без знака.
    • Предупреждения const-отмены: Если для функции в качестве параметра требуется тип const, переопределение может привести к ошибкам и непредсказуемому поведению. Причина предупреждения заключается в том, чтобы убедиться, что параметр const остается нетронутым и учитывает ограничения при разработке определенного API или функции.
    • Предупреждения о несовместимом указателе или параметре: Игнорируя это предупреждение, часто можно скрыть ошибки, которые будет трудно отслеживать позже. Устранение этих предупреждений может помочь сократить время диагностики других проблем с приложением.
  • Настройка согласованного конвейера CI/CD является ключевым фактором для устойчивого долгосрочного управления приложениями, так как она позволяет легко создавать двоичные файлы и соответствующие символы для отладки старых выпусков приложений. Правильная стратегия ветвления также необходима для отслеживания выпусков и позволяет избежать дорогостоящего места на диске при хранении двоичных данных.
  • По возможности определите все общие фиксированные строки как global const char* вместо жесткого программирования (например, в printf командах), чтобы их можно было использовать в качестве указателей данных во всей базе кода, обеспечивая при этом более пригодный для обслуживания код. В реальных приложениях сбор общего текста из журналов или операций со строками (например OK, , Succeededили имена свойств JSON) и его глобализация в константы часто приводили к экономии в разделе памяти данных только для чтения (также известном как rodata), что приводит к экономии флэш-памяти, которая может использоваться в других разделах (например, в тексте для дополнительного кода). Этот сценарий часто упускается из виду, но он может привести к значительной экономии флэш-памяти.

Примечание

Это также можно сделать путем простой активации оптимизации компилятора (например, -fmerge-constants для gcc). При выборе такого подхода также проверьте выходные данные компилятора и убедитесь, что применены необходимые оптимизации, так как они могут отличаться в разных версиях компилятора.

  • Для глобальных структур данных по возможности рекомендуется предоставлять фиксированную длину достаточно небольшим членам массива, а не использовать указатели на динамически выделяемую память. Например:
    typedef struct {
      int chID;
      ...
      char chName[SIZEOF_CHANNEL_NAME]; // This approach is preferable, and easier to use e.g. in a function stack.
      char *chName; // Unless this points to a constant, tracking a memory buffer introduces more complexity, to be weighed with the cost/benefit, especially when using multiple instances of the structure.
      ...
    } myConfig;
  • По возможности избегайте динамического выделения памяти, особенно в часто вызываемых функциях.
  • В C найдите функции, которые возвращают указатель на буфер памяти, и рассмотрите возможность преобразования их в функции, которые возвращают указатель буфера, на который указывает ссылка, и его связанный размер для вызывающих элементов. Причина этого заключается в том, что возврат только указателя на буфер часто приводил к проблемам с вызывающим кодом, так как размер возвращаемого буфера не признается принудительно и, следовательно, может поставить под угрозу согласованность кучи. Например:
    // This approach is preferable:
    MY_RESULT_TYPE getBuffer(void **ptr, size_t &size, [...other parameters..])
    
    // This should be avoided, as it lacks tracking the size of the returned buffer and a dedicated result code:
    void *getBuffer([...other parameters..])

Динамические контейнеры и буферы

Контейнеры, такие как списки и векторы, также часто используются во встроенных приложениях C. С предупреждением, что из-за ограничений памяти при использовании стандартных библиотек они обычно должны быть явно закодированы или связаны как библиотеки. Эти реализации библиотек могут привести к интенсивному использованию памяти, если они не разработаны тщательно.

Помимо стандартных статически выделенных массивов или динамических реализаций с большим объемом памяти, мы рекомендуем использовать метод добавочного распределения. Например, начните с пустой очереди реализации N предварительно выделенных объектов; при отправке (N+1)-й очереди очередь увеличивается на фиксированный X дополнительный предварительно выделенный объект (N=N+X), который будет динамически выделяться до тех пор, пока другое дополнение к очереди не переполнит ее текущую емкость и не увеличит выделение памяти дополнительными предварительно выделенными объектами X. В конечном итоге вы можете реализовать новую функцию сжатия для экономного вызова (так как это было бы слишком дорого вызывать на регулярной основе), чтобы освободить неиспользуемую память.

Выделенный индекс динамически сохраняет число активных объектов для очереди, которое может быть ограничено максимальным значением для дополнительной защиты от переполнения.

Такой подход устраняет "болтовни", создаваемые непрерывным выделением памяти и освобождением в традиционных реализациях очередей. Дополнительные сведения см. в разделе Управление памятью и использование. Аналогичные подходы можно реализовать для таких структур, как списки, массивы и т. д.