Я Вашу мысль понял, но там есче нужно после сдвига сделать вот это
Разбейте массив current[SIZE] на две части current[2][SIZE/2], заведите доп. переменную индекса i1 и после каждой итерации меняйте части массива местами: i1 ^= 1; Тогда не надо будет ничего "двигать".
Иногда лучше сделать memcpy() чем в цикле на каждой итерации делать такое. Лучше по скорости работы. Автору нужна скорость, а такое управление индексом добавит кучу лишних тактов в цикл. Много больше чем memcpy() на больших выровненных массивах. Для скорости будет лучше сделать memcpy(). А если ещё и 16-битные значения в массивах всё-таки знаковые или беззнаковые, но в диапазоне 0...32767 и работает на Cortex-M4 или выше, то круче будет:
Код:
int32_t sum = 0; uint32_t *p1 = &buf_U[0], *p2 = buf_I[SDVIG]; int i = 400 / 2; do sum = __SMLAD(*p1++, *p2++, sum); while (--i);
И подровнять расположение массивов на границу 32 бит. Да и если, как пишет автор, памяти не жалко, то кольцевые массивы можно кратно увеличить уменьшив также кратно количество вызовов memcpy(). Ну или организовать данные в массивах как я выше описал.
Последний раз редактировалось jcxz Пт дек 22, 2017 13:24:09, всего редактировалось 2 раз(а).
такое управление индексом добавит кучу лишних тактов в цикл. Много больше чем memcpy() на больших выровненных массивах.
только вы забываете, что у автора обработка в цикле ПОСЛЕ memcpy, т.е. все такты копирования пойдут плюсом к обработке. а управление индексом добавить по 2-3 такта на итерацию ПОЛЕЗНОГО цикла. так что это еще большой вопрос, что окажется быстрее по совокупности
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
такое управление индексом добавит кучу лишних тактов в цикл. Много больше чем memcpy() на больших выровненных массивах.
только вы забываете, что у автора обработка в цикле ПОСЛЕ memcpy, т.е. все такты копирования пойдут плюсом к обработке. а управление индексом добавить по 2-3 такта на итерацию ПОЛЕЗНОГО цикла. так что это еще большой вопрос, что окажется быстрее по совокупности
А вы компилировать пробовали, то что предложили? Где там 2-3 такта? Там все 20 будет на итерацию. Так как используется дополнительные манипуляции с индексом, то компилятор не сможет адресацию через индексы заменить на адресацию через указатели, а это добавит кучу команд вычисления адресов операций чтения и записи в каждой итерации не считая самой if(++index_I >= 400) index_I = 0; которая уже выльется как минимум в 4 команды. И это хорошо если компилятор использует операцию условного выполнения ITxxx, а если нет то поставит условный переход - это будет ещё хуже. Без этого компилятор заменит все ваши индексные операции с массивами на операции с указателями и на каждой итерации для чтения будет использовать только по две команды LDRH Rx, [Rx], #2. Если имеется if(++index_I >= 400) index_I = 0; то он не сможет использовать указатели вычисленные перед началом цикла. Кроме того данные у автора 16-битные, а memcpy() внутри оптимизирована и на больших массивах, выровненных на границу 32бит, будет копировать по 8 шт. 32-битных слов за итерацию, т.е. - будет тратить всего ~ 2 такта на копирование каждой пары значений. А если ещё как я писал, кратно увеличить размеры массивов, то во столько же крат можно уменьшить количество вызовов memcpy().
Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей.
Кроме того данные у автора 16-битные, а memcpy() внутри оптимизирована и на больших массивах, выровненных на границу 32бит, будет копировать по 8 шт. 32-битных слов за итерацию, т.е. - будет тратить всего ~ 2 такта на копирование каждой пары значений.
собственно, с этого я и начал, так что не надо мне об этом рассказывать.
а все наши с вами рассуждения могут ничего не стоить, если компилятор хорошо оптимизирует.
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
а все наши с вами рассуждения могут ничего не стоить, если компилятор хорошо оптимизирует.
По-любому лучшая оптимизация индексных обращений к массивам - замена и на обращения через указатели, вычисленные перед циклом. А в теле цикла - работа с этими указателями командами с автоинкрементом. Но если есть модификация индексов внутри цикла, то заменить на указатели компилятор не сможет. А значит на каждой итерации будет заново считать исполнительные адреса для каждого операнда. Вот во что компилируется приведённый мной код:
Код:
u32 *p1 = &d.sh.bp[0], sum = 0; enum {SDVIG = 100}; int i = 400 / 2; do { sum = __SMLAD(p1[0], p1[SDVIG], sum); p1++; } while (--i);
Этого сделать нельзя так, как в зависимости от частоты количество измерений на период разное и в идиале составляет 400шт, но постоянно плавает.
Ну и что? Создаёте массив скажем примерно в 10 раз больше = 4000 значений. А это значит, что если в кольцевой буфер размером 400 значений каждый раз добавлять по примерно 400 новых значений, то значит примерно на одно добавление будет примерно одно пересечение границы. А если размер == 4000 значений, то значит пересечение будет примерно на каждое 10-е добавление. А делать memcpy() нужно только при пересечении границы массива. Так что без разницы - что фиксированное кол-во добавлять, что переменное.
Предположим есть некая переменная v16, она задействована в функциях различных прерываний. Также эта переменная используется для связи с главным циклом программы.
По уму она должна быть объявлена следующим образом:
Код:
volatile uint16_t v16;
Такая запись исключает какую-либо оптимизацию над этой переменной.
Прерывания у меня не вложенные, имеют одинаковый приоритет и не могут прерывать друг друга.
Я подумал, а нельзя ли включить оптимизацию над этой переменной в обработчиках прерываний и выключить в главном цикле. Всё это затевается чтобы немного сократить время выполнения обработчиков. Решил написать примерно так:
Разумеется данный кусок кода был придуман и выложен только лишь для примера. Код реального проекта сложнее и запутаннее. Он реализует через прерывания определение состояния, вычисление угловой позиции механизма и формирование управляющих воздействий. В главном цикле содержится "медленный" алгоритм, который пропускает полученные данные через модель и корректирует управление. Также осуществляется связь с компьютером.
Данные о состоянии механизма записаны в структуре примерно следующего вида:
Код:
typedef struct{ volatile uint8_t state_machine; // машина состояний int16_t angle_cnt; // текущая угловая позиция volatile uint8_t event; // события uint32_t rev_period_cnt; // счётчик периода оборота диска энкодера volatile uint32_t rev_period_latch; // период последнего полного оборота uint32_t rev_period_pred; // предсказание периода следующего оборота // и ещё десяток параметров }ENGINE_PARAMS;
ENGINE_PARAMS engine_data;
Например переменная состояния state_machine используется как в конечном автомате в прерываниях, так и для управления из главного цикла. Переменная rev_period_latch используется в прерываниях для вычисления ускорений, угловых и временных моментов управления. Она же используется в главном цикле для корректировки управления.
Такой доступ к данным получается не очень оптимальным. Я пока придумал 2 варианта.
Вариант 1. Все поля структур какие надо объявляются как volatile. Внутри функций они копируются во временные переменные, которым разрешена оптимизация. На выходе копируются обратно. Например:
Код:
void proc_engine_state( void ) { uint8_t sm = engine_data.state_machine;
// делать что-то с переменной sm
engine_data.state_machine = sm; }
Вариант 2. В структуре ENGINE_PARAMS все данные не volatile и из функций прерываний к ним идёт самый обычный доступ.
Код:
typedef struct{ uint8_t state_machine; // машина состояний int16_t angle_cnt; // текущая угловая позиция uint8_t event; // события uint32_t rev_period_cnt; // счётчик периода оборота диска энкодера uint32_t rev_period_latch; // период последнего полного оборота uint32_t rev_period_pred; // предсказание периода следующего оборота // и ещё десяток параметров }ENGINE_PARAMS;
Но объявляется дополнительный указатель
Код:
volatile ENGINE_PARAMS *p_engine_data;
через который получается volatile доступ к членам структуры из главного цикла:
Код:
ENGINE_PARAMS engine_data; // вот это используется только прерываниями volatile ENGINE_PARAMS *p_engine_data;// а это для главного цикла
Не знаю что ты там делаешь и что хочешь, но методом вангования предложу способ оптимизации скорости кода, если хочешь ускорить работу с некоторой переменной обьяви ее с директивой register компилятор закрепит ее в регистре общего назначения это ускорит работу кода. Только смотри регистры не резиновые )))
_________________ Инженер R@D
Telegram чат: https://t.me/radiowolf или в поиске приложения @radiowolf. Личка:@cncoxford
В главном цикле содержится "медленный" алгоритм, который пропускает полученные данные через модель и корректирует управление. Также осуществляется связь с компьютером.
можно воткнуть какйюнить RTOS, умеющую в кванты времени и приоритеты тредов, в прерывании выставлять флаги готовности к работе приоритетных задач, "медленный алгоритм" задать низкоприоритетным.
Да, компилятор ARMCC, но это наверно не имеет значения т.к. везде должно быть одинково.
стандарт ничего особо не говорит про такие финты ушами, кроме как UB при касте volatile к не-volatile. В предыдущем минимальном примере gcc и clang оставляют тело цикла, sdcc - выбрасывает
Oxford писал(а):
если хочешь ускорить работу с некоторой переменной обьяви ее с директивой register компилятор закрепит ее в регистре общего назначения
или нет. Компилятор в общем случае не обязан этого делать:
Цитата:
A declaration of an identifier for an object with storage-class specifier register suggests that access to the object be as fast as possible. The extent to which such suggestions are effective is implementation-defined. The implementation may treat any register declaration simply as an auto declaration.
или нет. Компилятор в общем случае не обязан этого делать
Этот register компилятор скорее всего проигнорит, т.к. в С++17 код с ним уже даже не компилится, а до этого он долго был depricated и игнорировался, потому учитывая общую кодовую базу компиляторов можно ожидать такое поведение и в С, но в том же gсс есть еще другая форма записи, с привязкой к конкретному регистру, вот тот register точно работает.
C99 вводит типы данных с быстрым доступом, например, fast_int8_t... по идее компилятор должен подобрать для переменной этого типа самый быстрый способ доступа... т.е. типозависимая какая-то оптимизация должна быть. для ARM родным является 32-битный тип, и очень может быть, именно он окажется самым быстрым... т.к. я никогда не работал с ARM, мне неведомо, как отреагирует на fast_int8_t ваш компилятор... но попробуйте.
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
в C - нет. Та цитата из стандарта (черновика) C11. C++ - другой язык, а не "С с классами"
Разве я сказал, что да? Просто в С++11 register стал depricated, т.к. в большинстве случаев он и так игнорировался, потому такое же поведение можно ожидать и от С. Это хинт, чем продвинутее компилятор, тем меньше в нем необходимости, кроме того использование глобального register привносит ряд ограничений, например, у меня с ним не работает LTO.
C99 вводит типы данных с быстрым доступом, например, fast_int8_t... по идее компилятор должен подобрать для переменной этого типа самый быстрый способ доступа...
строго говоря, не способ доступа, а быстрее всего обрабатываемый тип. Довольно вероятно, что компилятор его в конце концов использует и для простого uint8_t.
строго говоря, не способ доступа, а быстрее всего обрабатываемый тип
ну я хотел донести следующую мысль: если тип должен быстрее всего обрабатываться, то переменная этого типа должна попасть в такую область хранения, которая эту самую быстроту способна обеспечить. например, такая переменная может иметь приоритет перед остальными для хранения в регистре...
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
C99 вводит типы данных с быстрым доступом, например, fast_int8_t... по идее компилятор должен подобрать для переменной этого типа самый быстрый способ доступа... т.е. типозависимая какая-то оптимизация должна быть. для ARM родным является 32-битный тип, и очень может быть, именно он окажется самым быстрым... т.к. я никогда не работал с ARM, мне неведомо, как отреагирует на fast_int8_t ваш компилятор... но попробуйте.
Да, потому лучше эти fast не использовать...
Код:
uint8_t aa = 0; uint_fast8_t bb = 0; aa = ~aa; // 0xFF bb = ~bb; // 0xFFFF'FFFF
Потому что смысл register в обоих языках одинаковый, а компилятор по сути один, если не считать чистых сишных компиляторов, которые обычно проще и с поддержкой новых стандартов там похуже, потому для них register может до сих пор что-то значит.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 13
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения