haker_fox 61 13 октября, 2021 Опубликовано 13 октября, 2021 · Жалоба 12.10.2021 в 14:22, jcxz сказал: единый общий класс, то он будет очень неоптимальным. Возможно, что я Вас неправильно понял. Но считаю, что это возможно. Любой последовательный порт позволяет: 1. Открыть себя (с настройкой общепринятыми параметрами: скорость, количество бит данных и т.д.). 2. Писать в себя. 3. Читать из себя. 4. Закрыть себя (не использую). А все нюансы конкретного железа запихать в конкретный класс драйвера. Это называется инкапсуляция. А наружу будет торчать условно четыре метода класса, описанные выше. И интерфейс будет одинаковым. И при подмене класса драйвера в пользовательском ПО разницы не будет. При этом единый класс - это абстрактный класс дарйвра. Который сам ничего не умеет, но предоставляет интерфейс, который должен реализовать конкретный класс драйвера. Пример моего абстрактного класса. З.Ы. Не знаю как закатать под спойлер((( З.Ы.Ы. Не эксперт в Си++, потому качество кода не претендует на 5 с плюсом) Скрытый текст #pragma once #ifdef __cplusplus #include "libs\ret_vals.hpp" #include "drv\bus\serial\base_serial_bus_drv.hpp" #include <stdio.h> namespace Drv { //template <size_t TxBuffSize, size_t RxBuffSize> class BaseUsart : public BaseSerialBus { public: enum class Baudrate { B19200 = 19200, B115200 = 115200, B921600 = 921600, B1280000 = 1280000, }; enum class Parity : uint8_t { No, Odd, Even, Mark, Space, }; enum class DataBits : uint8_t { Four, Five, Six, Seven, Eight, }; enum class StopBits : uint8_t { One, Onehalf, Two, }; enum class TransferMethod : uint8_t { Polling, Interrupt, Dma, }; struct Settings { Baudrate baudrate = Baudrate::B115200; Parity parity = Parity::No; DataBits dataBits = DataBits::Eight; StopBits stopBits = StopBits::One; TransferMethod txMethod = TransferMethod::Interrupt; TransferMethod rxMethod = TransferMethod::Interrupt; }; static const auto GENERAL_TIMEOUT_MS = 300; virtual RetVal write( const void * const src, const size_t count, size_t * const wr = nullptr, const size_t timeout_ms = GENERAL_TIMEOUT_MS ) = 0; virtual RetVal writeDirectly( const void * const src, const size_t count ) {return RetVal::Failed;} virtual RetVal read( void * dst, const size_t count, size_t * const rd = nullptr, const size_t timeout_ms = GENERAL_TIMEOUT_MS ) = 0; virtual void flush() = 0; virtual RetVal setSettings( const Settings settings ) = 0; virtual Settings getSettings() const { return m_settings; } protected: BaseUsart( const BusNum num ) : BaseSerialBus(num) {} struct Msg { size_t txCount = 0; const char * pSrc = nullptr; }; Settings m_settings; private: }; }; #endif Реализация конкретного драйвера последовательного порта. Скрытый текст #pragma once #ifdef __cplusplus #include "base_usart_drv.hpp" #include "drv\mcu\clk\stm32f0x1_rcc_drv.hpp" #include "libs\usefulmacro.hpp" #include "drv\mcu\interrupts\cortex_mx_nvic.hpp" #include "drv\mcu\gpio\stm32f0x1_gpio_drv.hpp" #include "ffwk\libs\debug\config_assert.hpp" #include <ring_buffer_os_ext.h> //static USART_TypeDef * const regArray[] = { // USART1, USART2, USART3, USART4, // USART5, USART6, USART7, USART8, //}; namespace Drv { template <BaseSerialBus::BusNum Num, const size_t RxSize, typename RxPin, Drv::Pin::Function RxAF, typename TxPin, Drv::Pin::Function TxAF> class Stm32f0x1Usart : public BaseUsart { public: #if defined (STM32F091) static const auto BUS_NUM = 8; static_assert( Num > BusNum::N0 && Num < BusNum::N9, "Invalid usart number!" ); #elif defined (STM32F051) static const auto BUS_NUM = 2; static_assert( Num == BusNum::N1 || Num == BusNum::N2, "Invalid usart number!" ); #elif defined (STM32F030) static const auto BUS_NUM = 1; static_assert( Num == BusNum::N1, "Invalid usart number!" ); #else #error "No MCU is selected!"; #endif Stm32f0x1Usart() : BaseUsart(Num) {} RetVal open() override { if (m_open == RetVal::Ok) return m_open; /// TODO automatically choose vector based on template args Nvic::install(USART1_IRQn, irqHandler); setNvic(); enableClock(); m_open = setSettings(); return m_open; } RetVal close() override { return RetVal::Ok; } RetVal write( const void * const src, const size_t count, size_t * const wr, const size_t timeout_ms ) override { if (wr) *wr = count; if (!count) return RetVal::Ok; if (count == 1) { m_ctrl.pRegs->TDR = *static_cast<const char *>(src); while (!( m_ctrl.pRegs->ISR & USART_ISR_TXE )); return RetVal::Ok; } auto &msg = m_ctrl.msg; msg.txCount = count; msg.pSrc = reinterpret_cast<const char *>(src); m_ctrl.pRegs->CR1 |= USART_CR1_TXEIE; auto const result = m_ctrl.event.wait(timeout_ms) ? RetVal::Ok : RetVal::TimeOut; if (result != RetVal::Ok && wr) *wr = 0; return result; } RetVal writeDirectly( const void * src, size_t count ) override { decltype( auto )ptr = static_cast<const char *>(src); while (count--) { m_ctrl.pRegs->TDR = *ptr++; while (!( m_ctrl.pRegs->ISR & USART_ISR_TXE )); } return RetVal::Ok; } RetVal read( void * dst, const size_t count, size_t * const rd, const size_t timeout_ms ) override { if (count == 0) { if (rd) *rd = count; return RetVal::Ok; } char * cDst = reinterpret_cast<char *>(dst); const auto result = m_ctrl.rx.read(cDst, count, rd, timeout_ms); return result ? RetVal::Ok : RetVal::TimeOut; } void flush() override { m_ctrl.pRegs->CR1 &= ~USART_CR1_TXEIE; while (!( m_ctrl.pRegs->ISR & USART_ISR_TXE )); while (m_ctrl.pRegs->ISR & USART_ISR_RXNE) volatile auto dummy = m_ctrl.pRegs->RDR; m_ctrl.msg = Msg(); m_ctrl.event.clear(); m_ctrl.rx.flush(); } RetVal setSettings( const Settings settings = Settings() ) override { m_settings = settings; setNvic(); m_ctrl.pRegs->CR1 &= ~USART_CR1_UE; m_ctrl.pRegs->CR1 = 0 | USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE ; //m_ctrl.pRegs->CR3 |= USART_CR3_OVRDIS; const auto pclk = Stm32f0x1Rcc::getPeriphClk(); m_ctrl.pRegs->BRR = pclk / static_cast<size_t>(m_settings.baudrate); m_ctrl.pRegs->CR1 |= USART_CR1_UE; setNvic(Nvic::Prio::Highest, true); return RetVal::Ok; } protected: struct ControlData { USART_TypeDef * const pRegs; Msg msg; OS::TEventFlag event; OS::RingBuffer<char, RxSize> rx; // TaskHandle_t tskHndl; // size_t rxcounter; // DmaCfg::PeriphConfig dmaTx; // DmaCfg::PeriphConfig dmaRx; // DmaDriver &dma; // std::uint32_t dummyRx; ControlData() : pRegs(( []() { static USART_TypeDef * const regArray[] = { USART1, USART2, USART3, USART4, USART5, USART6, USART7, USART8, }; return regArray[static_cast<size_t>(Num) - 1]; } )()) { TxPin::mode(Drv::Pin::Mode::Alternate); RxPin::mode(Drv::Pin::Mode::Alternate); TxPin::setAlternate(TxAF); RxPin::setAlternate(RxAF); } }; Stm32f0x1Usart( const Stm32f0x1Usart & ) = delete; Stm32f0x1Usart( const Stm32f0x1Usart && ) = delete; void enableClock() { OsApi::CritSecSmart cs; switch (m_busnum) { case BusNum::N1: RCC->APB2ENR |= RCC_APB2ENR_USART1EN; return; case BusNum::N2: RCC->APB1ENR |= RCC_APB1ENR_USART2EN; return; case BusNum::N3: RCC->APB1ENR |= RCC_APB1ENR_USART3EN; return; case BusNum::N4: RCC->APB1ENR |= RCC_APB1ENR_USART4EN; return; case BusNum::N5: RCC->APB1ENR |= RCC_APB1ENR_USART5EN; return; case BusNum::N6: RCC->APB2ENR |= RCC_APB2ENR_USART6EN; return; case BusNum::N7: RCC->APB2ENR |= RCC_APB2ENR_USART7EN; return; case BusNum::N8: RCC->APB2ENR |= RCC_APB2ENR_USART8EN; return; default: configASSERT(0); } } void setNvic( const Nvic::Prio prio = Nvic::Prio::Lowest, const bool on = false ) { IRQn irqn; switch (m_busnum) { case BusNum::N1: irqn = USART1_IRQn; break; case BusNum::N2: irqn = USART2_IRQn; break; default: #if defined (STM32F091) irqn = USART3_8_IRQn; #endif break; } Nvic::disable(irqn); Nvic::clearPending(irqn); if (on) { Nvic::setPriority(irqn, prio); Nvic::enable(irqn); } } #pragma optimize=no_size_constraints __forceinline static void irqHandler() { OS::TISRW tisr; auto * const pRegs = m_ctrl.pRegs; auto &msg = m_ctrl.msg; while (pRegs->ISR & USART_ISR_RXNE) m_ctrl.rx.push_isr(pRegs->RDR); if (( pRegs->ISR & USART_ISR_TXE ) && msg.txCount) { if (--msg.txCount == 0) { pRegs->CR1 &= ~USART_CR1_TXEIE; m_ctrl.event.signal_isr(); } pRegs->TDR = *msg.pSrc++; } } static const uint32_t INTERRUPT_MASK = USART_ISR_TXE | USART_ISR_RXNE; static ControlData m_ctrl; }; template <BaseSerialBus::BusNum Num, const size_t RxSize, typename RxPin, Drv::Pin::Function RxAF, typename TxPin, Drv::Pin::Function TxAF> Stm32f0x1Usart<Num, RxSize, RxPin, RxAF, TxPin, TxAF>::ControlData Stm32f0x1Usart<Num, RxSize, RxPin, RxAF, TxPin, TxAF>::m_ctrl; }; #endif 12.10.2021 в 14:41, jcxz сказал: Всё равно в конце-концов придётся или обновлять или этот "сабмодуль" распадётся на 2 параллельные ветки. Так их в любом случае нужно вносить эти исправления. Тут уже управление кодом больше встаёт вопрос. 12.10.2021 в 14:41, jcxz сказал: размножение веток "сабмодуля" на множество параллельных А зачем ветки, если есть тэги и коммиты в нотации гита? С другими системами контроля версий не работал, но подозреваю, что что=то аналогичное там есть. Ветки плодить вообще не очень хорошо. Т.к. их нужно будет когда-то синхронизировать. А это уже аналог кучи папок и архивов от чего нужно уходить. Обычно в параллельной ветке, например названной develop, создают какие-то новые фичи. Предварительно "отбранчевав" её от master'а. Потом делают слияние данной ветки с мастером, чтобы новые наработки в него попали. Но это не значит, что все проекты с мастером сразу изменились, т.к. ссылаются на определённый коммит. 12.10.2021 в 14:41, jcxz сказал: ещё тестирование в испытательной лаборатории (несколько недель) Это всё я понимаю, т.к. работаю в данной сфере) И по объектам тоже иногда езжу. Другое дело, что всё это уже не влияет на форму ведения архива с кодом. Можно ничего не исправлять. Но в независимости от того, каким образом исправили, испытания делать в любом случае. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 13 октября, 2021 Опубликовано 13 октября, 2021 · Жалоба 10 hours ago, jcxz said: Оказывается товарищи-ООП-программисты перетащили все свои исходники с ПК "как есть". Ха-ха! Как мне знакома Ваша ситуация) Когда некоторые коллеги тянут подходы для программирования ПК в мир встраиваемых систем))) И потом оказывается, что STM32F091 не хватает по ресурсам (всего 32 кБ ОЗУ). Вместо него ставится двухядерный LPC4337 + 32 Мб SDRAM. К слову, память в проекте оказалась нелишней (много данных собирается, хотя можно было и обойтись гораздо меньшим объёмом не в ущерб функциональности прибора и немного по другому используя периферию). А вот ресурсов процессора выше крыши. Непрофессионально. Зато остальные программисты теперь в проекте используют new/delete, векторы, листы, std::map. И стэк мы уже увеличили для нескольких задач FreeRTOS до почти мегабайта. И размер кучи в скриптел линкера уже 2 Мб! Крассссота! 9 hours ago, Arlleex said: грешу всякими if(i > 0 && --i) и т.д. Читал у "Алена c++" в своё время, что так делать не надо) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 173 13 октября, 2021 Опубликовано 13 октября, 2021 · Жалоба 2 часа назад, haker_fox сказал: Читал у "Алена c++" в своё время, что так делать не надо) Почему же? В соответствии со стандартом данная строчка не вызывает криминала А то, о чем Вы говорите, скорее всего было о конструкциях вида i = i++; что в C++, вроде как, теперь не UB. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 13 октября, 2021 Опубликовано 13 октября, 2021 · Жалоба 13 minutes ago, Arlleex said: вида i = i++; И это тоже. 14 minutes ago, Arlleex said: В соответствии со стандартом данная строчка не вызывает криминала Я понимаю, что скомпилируется и будет работать. Тем не менее немного затрудняет понимание) Впрочем, дело вкуса) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
turnon 1 13 октября, 2021 Опубликовано 13 октября, 2021 · Жалоба On 10/12/2021 at 2:01 PM, one_eight_seven said: Это не проблема языка. Это проблема управления конфигурациями ПО. Уже, кстати, решена, и не раз. Именно. С релизом проекта надо фиксировать и версию библиотечного репозитория. А изменения в библиотечном репозитории уже переносятся в ранние версии процедурой слияния (merge). А иначе больше проблем будет от "супер Common" библиотеки. Проект - это набор всех файлов необходимых для сборки, причем согласованных версий, а не просто что-то там подтягиваем. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 173 15 октября, 2021 Опубликовано 15 октября, 2021 · Жалоба Следующий вопрос назрел. Наверное, больше по оформлению кода. Вот есть в классе какие-то приватные данные и методы для работы с ними. И вот есть среди таких данных переменные, отвечающие за состояние, например, FSM-автомата. В моем случае (протокол SLCAN) интерфейс может находиться в открытом, закрытом и режиме listen-only. Эти режимы (состояния) переключаются кодом парсера, который есть ни что иное, как публичный метод parse() моего класса (вызывать его я буду, когда что-то в буфер данных UART прилетит). Т.е. будет у меня так namespace SLCAN { enum eIFState : u32 { CLOSED, OPENED, LISTEN }; class cSlave { public: ... void parse(...); private: eIFState ifState; }; } И вот теперь вопрос: в дебрях parse() я буду менять состояния ifState, присваивая ему значения из eIFState. Но это как бы выглядит несколько убого, хотелось бы в исходнике видеть адекватные понятные названия действий, например close(), open(), listen(). Запихнув приватными методами вот такой сахар void open() { ifState = OPENED; } void close() { ifState = CLOSED; } void listen() { ifState = LISTEN; } , можно ли быть уверенным, что компилятор не создаст реальных функций, а встроит вызовы? То же самое касается функций каких-нибудь проверок, например, eIFState isIFOpened() { return ifState == OPENED; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 22 15 октября, 2021 Опубликовано 15 октября, 2021 · Жалоба 12 minutes ago, Arlleex said: можно ли быть уверенным, что компилятор не создаст реальных функций, а встроит вызовы? добавить перед названием метода ключевое слово "inline", что собственно очевидно )) можно усилить эффект, добавив еще и __attribute__((always_inline)) на примере ARM компилятора (у GCC вроде также). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 15 октября, 2021 Опубликовано 15 октября, 2021 · Жалоба 51 minutes ago, Arlleex said: , можно ли быть уверенным, что компилятор не создаст реальных функций, а встроит вызовы? Обычно у меня такое всегда прокатывает, если тело функции расположить в хидере. Но при необходимости я всегда контролирую листинг. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 231 15 октября, 2021 Опубликовано 15 октября, 2021 · Жалоба 57 минут назад, Arlleex сказал: можно ли быть уверенным, что компилятор не создаст реальных функций, а встроит вызовы? Нет. В си нет средств, чтобы явно указать: "сделать функцию обязательно inline". Всё на откуп компилятору - как он решит. Если он даже из просто куска кода функции, может сам создать отдельную подпрограмму (там где в исходнике никакой функции не было), то что говорить о действительно отдельных функциях? 5 минут назад, haker_fox сказал: Обычно у меня такое всегда прокатывает, если тело функции расположить в хидере. Но при необходимости я всегда контролирую листинг. Просто везёт. Даже если Вы напишите функцию: f() {} как макрос препроцессора: #define f() (...), даже в этом случае нет гарантий что в коде не окажется BL. Достаточно поставить максимальную оптимизацию по размеру, чтобы увидеть это воочию во многих участках кода. PS: Единственное что можно посоветовать - выделить такие участки кода (где вызываются inline-функции), в отдельный файл, и для него поставить максимальную оптимизацию по скорости или балансную. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 15 октября, 2021 Опубликовано 15 октября, 2021 · Жалоба 20 minutes ago, jcxz said: чтобы увидеть это воочию во многих участках кода. В моём случае это было в паре проектов в одном-двух местах. Поэтому контролировать было довольно просто. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 173 15 октября, 2021 Опубликовано 15 октября, 2021 · Жалоба 1 час назад, Forger сказал: добавить перед названием метода ключевое слово "inline", что собственно очевидно )) 27 минут назад, haker_fox сказал: Обычно у меня такое всегда прокатывает, если тело функции расположить в хидере. Но при необходимости я всегда контролирую листинг. Ну вот как раз в некоторых источниках утверждают, что определение функции в самом классе неявно подразумевает наличие inline. Т.е. class A { void func() { // some code } }; равносильно class A { inline void func(); }; inline void A::func() { // some code } Можно, конечно, попробовать __attribute__((always_inline)). Хотя (если я правильно понял написанное по ссылке выше), в C++ встроенные функции будут итак изо всех сил пытаться реально "встроиться". 27 минут назад, jcxz сказал: Нет. В си нет средств, чтобы явно указать: "сделать функцию обязательно inline". Всё на откуп компилятору - как он решит. Надо разбираться, но в C++ ключевое слово inline и все его средозависимые подобия имеет эффект гораздо сильнее, чем в Си. Что-то было в этом роде... Но пока не берусь утверждать. Возможно что различия совершенно не в этом, а в, например, поведении вкупе с extern/static... P.S. Ладно. Оставлю функциями, надеюсь компилятор не дурак, сделает по красоте. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 22 15 октября, 2021 Опубликовано 15 октября, 2021 · Жалоба 9 minutes ago, Arlleex said: Ну вот как раз в некоторых источниках утверждают, что определение функции в самом классе неявно подразумевает наличие inline. Т.е. Нет, по опыту это совсем не так. Нет никакого значения для компилятора где объявлена и где реализована функция Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 231 15 октября, 2021 Опубликовано 15 октября, 2021 · Жалоба 13.10.2021 в 05:14, haker_fox сказал: Возможно, что я Вас неправильно понял. Но считаю, что это возможно. Любой последовательный порт позволяет: 1. Открыть себя (с настройкой общепринятыми параметрами: скорость, количество бит данных и т.д.). 2. Писать в себя. 3. Читать из себя. 4. Закрыть себя (не использую). А все нюансы конкретного железа запихать в конкретный класс драйвера. Это называется инкапсуляция. А наружу будет торчать условно четыре метода класса, описанные выше. И интерфейс будет одинаковым. Я же вам описал пример для UART. Читали или нет? Повторю: В одном МК для инита UART-а нужно ему передать только указатель на блок регистров UART и скорость. В другом МК, для драйвера с аналогичным функционалом - ещё пару параметров к предыдущим. Например: для драйвера необходимо FIFO, и в одном МК оно есть и больше никаких телодвижений делать не надо; в другом МК (вспоминаем всеми любимые "народные" МК ) - FIFO у UART нет, и его нужно создать посредством DMA, соответственно - в функцию инициализации передавать номер DMA-канала. А в другом проекте - нужно ещё и формат UART-а (кол-во бит, чётность, ...) иметь возможность установить. Чтобы сделать универсальное, нужно везде городить функции с множеством передаваемых параметров. Отсюда и оверхед (в том МК, где достаточно было только скорости). Можно конечно передавать в качестве аргумента структуру, но опять будет оверхед, особенно когда встретится задача, которой нельзя передать const-структуру. Другой пример: в одном проекте у вас только одна задача работает с UART, а в другом проекте - к UART возможны обращения из разных задач. Так городить вам в вашем универсальном драйвере семафоры для доступа функционалу UART или нет? А ведь в третьем проекте может вообще потребоваться работа без ОС (или в режиме "с ОС" и "без ОС" в пределах одного проекта). И вот добвите Вы все эти семафоры и пр. в проект, и в проекте где они не нужны, получите оверхед. Цитата Пример моего абстрактного класса. Вот как раз на вашем примере и виден оверхед: Зачем член m_settings? А если мне он не нужен? А ОЗУ на него всё равно будет потрачена. Или зачем функция write() возвращает какое-то значение? Ну и многое другое.... 13.10.2021 в 05:40, haker_fox сказал: Зато остальные программисты теперь в проекте используют new/delete, векторы, листы, std::map. И стэк мы уже увеличили для нескольких задач FreeRTOS до почти мегабайта. И размер кучи в скриптел линкера уже 2 Мб! Самое веселье начнётся, когда выяснится, что где-то происходят утечки этой памяти. Особенно если девайс работает в режиме 24/7. Вот тогда эта кажущаяся простота куч и аукнется длительными поисками места утечки.... 22 минуты назад, haker_fox сказал: В моём случае это было в паре проектов в одном-двух местах. Поэтому контролировать было довольно просто. Вы же не знаете критериев, по которым компилятор решает - инлайнить или нет? А он это решает например по наличию похожих кусков кода. И завтра Вы, в совершенно другом месте исходника (да даже не Вы сами, а коллега) допишете где-то какую-то строчку кода, компилятор увидит, что код из неё получается подобный уже имеющемуся коду. И создаст из этого кода отдельную функцию, поставив BL в обоих местах. Так что если "контролировать" - то после каждой минимальной правки исходника, после каждой компиляции. Веселье ещё то.... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 231 15 октября, 2021 Опубликовано 15 октября, 2021 · Жалоба 46 минут назад, Arlleex сказал: Надо разбираться, но в C++ ключевое слово inline и все его средозависимые подобия имеет эффект гораздо сильнее, чем в Си. Что-то было в этом роде... Но пока не берусь утверждать. И не надо. Уже >20 лет пишу только на с++ - про него и говорю. Пример (IAR) - функция (написана в .h-файле): #pragma inline = forced inline u32 AtomicOr(u8 volatile *ptr, u32 arg) { u32 r, i1, i2; asm( "p01: LDREXB %0, [%3] \n" " ORRS %1, %0, %4 \n" " STREXB %2, %1, [%3]\n" " CMP %2, #0 \n" " BNE p01 " : "=&r"(r), "=&r"(i1), "=&r"(i2) : "r"(ptr), "r"(arg) : "cc", "memory"); i1 = i1; i2 = i2; return r; } Даже не просто inline, а ещё и #pragma inline = forced! Ставим оптимизацию на LOW, получаем: _Z12ConfigMdfNtfm: (+1) 0xB580 PUSH {R7,LR} AtomicOr(&configRst, map); 0x0001 MOVS R1,R0 0x.... LDR.N R0,??DataTable20_15 0x.... 0x.... BL _Z8AtomicOrPVtm Ставим оптимизацию MEDIUM: _Z12ConfigMdfNtfm: (+1) 0xB510 PUSH {R4,LR} AtomicOr(&configRst, map); 0x.... LDR.N R4,??DataTable26_8 ??p01: (+1) 0xE8D4 0x3F5F LDREXH R3, [R4] 0xEA53 0x0200 ORRS R2, R3, R0 0xE8C4 0x2F51 STREXH R1, R2, [R4] 0x2900 CMP R1, #0 0xD1F7 BNE ??p01 Т.е. - даже forced не помогает. Цитата P.S. Ладно. Оставлю функциями, надеюсь компилятор не дурак, сделает по красоте. Дурак он. Вот у меня программа, делает тяжёлые вычисления, соответственно оптимизация - по скорости. Но есть участки, где скорость выполнения неважна (инициализация например). И в этой инициализации есть заполнение каких-то массивов. В цикле конечно. А этот дурак циклы эти разворачивает! Создавая портянки кода из нескольких десятков инструкций там где совершенно не нужно. А мог бы догадаться..... Или например - в функции в начале и в конце есть обращение к некоей константе. Итого = 2 раза. И этот дурак (иных эпитетов не находится ), сохраняет значение указателя в регистре на протяжении всей функции. Раздувая таким образом список сохраняемых в PUSH/POP регистров. А ведь гораздо оптимальнее - просто заново загрузить этот указатель там где он потребовался 2-й раз и не тратить регистр. Но что тут поделаешь - ДУРАК! А ведь бывает намного хужее. Например: в одном месте кода вызываются 2 функции. Обе тяжёлые - долго выполняются, редко вызываются. Соответственно - цена входа/выхода в них ничтожна на общем времени выполнении и толку от их инлайна = 0. Но он, собака, их инлайнит! И не думает даже, что и в той и в другой функции есть большие массивы на стеке. И когда функции вызывались последовательно, то массивы занимали одно и то же место в ОЗУ и всё было тип-топ. А когда они заинлайнились, то компилятор вынес оба массива в родительскую функцию (забыв при этом про union). И получаем резкое увеличение требований к размеру стека! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 173 15 октября, 2021 Опубликовано 15 октября, 2021 · Жалоба 15 минут назад, jcxz сказал: ...компилятор вынес оба массива в родительскую функцию (забыв при этом про union). И получаем резкое увеличение требований к размеру стека! Да, такое мне тоже стреляло по коленям один разок на максимальной оптимизации. Лечил через атрибут noinline (не знаю, есть ли это в IAR, у меня Keil). Цитата Даже если Вы напишите функцию: f() {} как макрос препроцессора: #define f() (...), даже в этом случае нет гарантий... ИМХО, это зависит реально от того, насколько тривиальна та функция или дефайн. У меня, например, привычка уже выработалась в пуре-Си обзывать #define()-ми какие-то простейшие, но вполне логично обзываемые операции. Теперь эту манеру я хотел перетащить в C++, но #define() штука глобальная, это минус, а вот private-методы самое оно. Они "видны" там, где это нужно, и не более. Для меня это жирный плюс Почему только разработчики компиляторов не хотят сделать прагму __attribute__((boss_sredi_inline)), чтобы 100% инлайнил... Гадство Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться