Вопросы по С/С++ (СИ)

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Сообщение uk8amk »

Предположим есть некая переменная v16, она задействована в функциях различных прерываний. Также эта переменная используется для связи с главным циклом программы.

По уму она должна быть объявлена следующим образом:

Код: Выделить всё

volatile uint16_t v16;
Такая запись исключает какую-либо оптимизацию над этой переменной.

Прерывания у меня не вложенные, имеют одинаковый приоритет и не могут прерывать друг друга.

Я подумал, а нельзя ли включить оптимизацию над этой переменной в обработчиках прерываний и выключить в главном цикле. Всё это затевается чтобы немного сократить время выполнения обработчиков.
Решил написать примерно так:

Код: Выделить всё

#define	ACCESS_VOLATILE_U16(x)	(*((volatile uint16_t*) &x))
uint16_t v16;

void irq_handler1( void ) // прерывания
{
 v16 = 1;
}

void irq_handler2( void )
{
 v16 = 2;
}

main()
{
 ACCESS_VOLATILE_U16( v16 ) = 0;
 while(1)
 {
  abc = ACCESS_VOLATILE_U16( v16 );
 };	
}
Кто что думает на этот счёт?
Реклама
Друг Кота
Сообщения: 3092
Зарегистрирован: Пн апр 06, 2015 11:01:53
Откуда: москва, уфа

Сообщение arkhnchul »

имхо довольно бессмысленное занятие. Какие, по-вашему, оптимизации могут ускорить здесь выполнение обработчиков, при этом сохранив корректность?

sdcc, к примеру, совсем выкидывает из цикла строку abc = ACCESS_VOLATILE_U16( v16 ), если v16 не объявлена как volatile с самого начала.
Реклама
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Сообщение uk8amk »

Разумеется данный кусок кода был придуман и выложен только лишь для примера.
Код реального проекта сложнее и запутаннее.
Он реализует через прерывания определение состояния, вычисление угловой позиции механизма и формирование управляющих воздействий.
В главном цикле содержится "медленный" алгоритм, который пропускает полученные данные через модель и корректирует управление. Также осуществляется связь с компьютером.

Данные о состоянии механизма записаны в структуре примерно следующего вида:

Код: Выделить всё

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;// а это для главного цикла

main()
{
	p_engine_data->state_machine = STOP; // иниц значений
	p_engine_data->angle_cnt = 0;
	p_engine_data->rev_period_cnt = 0;
	while(1)
	{
		if( p_engine_data->state_machine == WORK )
		{
			putsf("ENGINE WORK");
		}else
		{
			putsf("ENGINE STOP");
		}
		if( p_engine_data->event == ERROR )
		{
			reinit_engine();
			putsf("ERROR");
		}
	}
}
Вариант 2 похож на тот, о котором говорил в предыдущем сообщении.

Кто как решает подобные вопросы? Просто объявлять нужные поля структуры как volatile и не заморачиваться?

Да, компилятор ARMCC, но это наверно не имеет значения т.к. везде должно быть одинково.

Ещё. Есть специализированные DSP процессоры, в которых моя задача решена на аппаратном уровне. Но мне они пока недоступны.
Опытный кот
Аватара пользователя
Сообщения: 819
Зарегистрирован: Вт окт 23, 2012 13:17:25
Откуда: Прокопьевск

Сообщение Oxford »

Не знаю что ты там делаешь и что хочешь, но методом вангования предложу способ оптимизации скорости кода, если хочешь ускорить работу с некоторой переменной обьяви ее с директивой register компилятор закрепит ее в регистре общего назначения это ускорит работу кода. Только смотри регистры не резиновые )))
Инженер R@D

Telegram чат: https://t.me/radiowolf или в поиске приложения @radiowolf. Личка:@cncoxford
Контактная информация:
Реклама
Эиком - электронные компоненты и радиодетали
Друг Кота
Сообщения: 3092
Зарегистрирован: Пн апр 06, 2015 11:01:53
Откуда: москва, уфа

Сообщение arkhnchul »

[uquote="uk8amk",url="/forum/viewtopic.php?p=3269085#p3269085"]Разумеется данный кусок кода был придуман и выложен только лишь для примера.
Код реального проекта сложнее и запутаннее.[/uquote]
приведите реальный код :dont_know:
[uquote="uk8amk",url="/forum/viewtopic.php?p=3269085#p3269085"]Он реализует через прерывания определение состояния, вычисление угловой позиции механизма и формирование управляющих воздействий.[/uquote]
он этим прямо в прерывании занимается? не то чтобы это было хорошей идеей.
[uquote="uk8amk",url="/forum/viewtopic.php?p=3269085#p3269085"]В главном цикле содержится "медленный" алгоритм, который пропускает полученные данные через модель и корректирует управление. Также осуществляется связь с компьютером.[/uquote]
можно воткнуть какйюнить RTOS, умеющую в кванты времени и приоритеты тредов, в прерывании выставлять флаги готовности к работе приоритетных задач, "медленный алгоритм" задать низкоприоритетным.
[uquote="uk8amk",url="/forum/viewtopic.php?p=3269085#p3269085"]Такой доступ к данным получается не очень оптимальным. Я пока придумал 2 варианта.[/uquote]
первый вариант более очевиден и имхо предпочтителен
[uquote="uk8amk",url="/forum/viewtopic.php?p=3269085#p3269085"]Да, компилятор ARMCC, но это наверно не имеет значения т.к. везде должно быть одинково.[/uquote]
стандарт ничего особо не говорит про такие финты ушами, кроме как UB при касте volatile к не-volatile. В предыдущем минимальном примере gcc и clang оставляют тело цикла, sdcc - выбрасывает :dont_know:
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.
Реклама
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="arkhnchul",url="/forum/viewtopic.php?p=3269167#p3269167"]или нет. Компилятор в общем случае не обязан этого делать[/uquote]
Этот register компилятор скорее всего проигнорит, т.к. в С++17 код с ним уже даже не компилится, а до этого он долго был depricated и игнорировался, потому учитывая общую кодовую базу компиляторов можно ожидать такое поведение и в С, но в том же gсс есть еще другая форма записи, с привязкой к конкретному регистру, вот тот register точно работает.
Реклама
Друг Кота
Сообщения: 3092
Зарегистрирован: Пн апр 06, 2015 11:01:53
Откуда: москва, уфа

Сообщение arkhnchul »

[uquote="Reflector",url="/forum/viewtopic.php?p=3269178#p3269178"]Этот register компилятор скорее всего проигнорит, т.к. в С++17 код с ним уже даже не компилится, а до этого он долго был depricated и игнорировался[/uquote]
в C - нет. Та цитата из стандарта (черновика) C11. C++ - другой язык, а не "С с классами" :dont_know:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18787
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

C99 вводит типы данных с быстрым доступом, например, fast_int8_t... по идее компилятор должен подобрать для переменной этого типа самый быстрый способ доступа... т.е. типозависимая какая-то оптимизация должна быть. для ARM родным является 32-битный тип, и очень может быть, именно он окажется самым быстрым... т.к. я никогда не работал с ARM, мне неведомо, как отреагирует на fast_int8_t ваш компилятор... но попробуйте.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="arkhnchul",url="/forum/viewtopic.php?p=3269187#p3269187"]в C - нет. Та цитата из стандарта (черновика) C11. C++ - другой язык, а не "С с классами" :dont_know:[/uquote]
Разве я сказал, что да? Просто в С++11 register стал depricated, т.к. в большинстве случаев он и так игнорировался, потому такое же поведение можно ожидать и от С. Это хинт, чем продвинутее компилятор, тем меньше в нем необходимости, кроме того использование глобального register привносит ряд ограничений, например, у меня с ним не работает LTO.
Друг Кота
Сообщения: 3092
Зарегистрирован: Пн апр 06, 2015 11:01:53
Откуда: москва, уфа

Сообщение arkhnchul »

[uquote="ARV",url="/forum/viewtopic.php?p=3269218#p3269218"]C99 вводит типы данных с быстрым доступом, например, fast_int8_t... по идее компилятор должен подобрать для переменной этого типа самый быстрый способ доступа...[/uquote]
строго говоря, не способ доступа, а быстрее всего обрабатываемый тип. Довольно вероятно, что компилятор его в конце концов использует и для простого uint8_t.
Reflector писал(а):потому такое же поведение можно ожидать и от С
почему вдруг?
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18787
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

arkhnchul писал(а):строго говоря, не способ доступа, а быстрее всего обрабатываемый тип
ну я хотел донести следующую мысль: если тип должен быстрее всего обрабатываться, то переменная этого типа должна попасть в такую область хранения, которая эту самую быстроту способна обеспечить. например, такая переменная может иметь приоритет перед остальными для хранения в регистре...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="ARV",url="/forum/viewtopic.php?p=3269218#p3269218"]C99 вводит типы данных с быстрым доступом, например, fast_int8_t... по идее компилятор должен подобрать для переменной этого типа самый быстрый способ доступа... т.е. типозависимая какая-то оптимизация должна быть. для ARM родным является 32-битный тип, и очень может быть, именно он окажется самым быстрым... т.к. я никогда не работал с ARM, мне неведомо, как отреагирует на fast_int8_t ваш компилятор... но попробуйте.[/uquote]
Да, потому лучше эти fast не использовать...

Код: Выделить всё

uint8_t aa = 0;
uint_fast8_t bb = 0;
aa = ~aa;  // 0xFF
bb = ~bb;  // 0xFFFF'FFFF
[uquote="arkhnchul",url="/forum/viewtopic.php?p=3269230#p3269230"]почему вдруг?[/uquote]
Потому что смысл register в обоих языках одинаковый, а компилятор по сути один, если не считать чистых сишных компиляторов, которые обычно проще и с поддержкой новых стандартов там похуже, потому для них register может до сих пор что-то значит.
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

Можно особо критичные к скорости функции перенести в ОЗУ, коли ARM.
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="Z_h_e",url="/forum/viewtopic.php?p=3269240#p3269240"]Можно особо критичные к скорости функции перенести в ОЗУ, коли ARM.[/uquote]
Разве что в CCM, из обычного ОЗУ исполняется медленнее.
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

Что такое CCM я не знаю, однако я тоже когда то считал что медленнее, приводил доводы по этому поводу и провел эксперимент, который мои доводы и опроверг. Конечно эксперимент был проведен на определенном камне (т.е. нельзя говорить за все), но скорость обуславливается шинами и скоростью памяти. Шины для конкретного ARM одни и те же, не важно в каком он камне, а флеш однозначно медленнее ОЗУ,
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Друг Кота
Сообщения: 3092
Зарегистрирован: Пн апр 06, 2015 11:01:53
Откуда: москва, уфа

Сообщение arkhnchul »

[uquote="ARV",url="/forum/viewtopic.php?p=3269232#p3269232"]если тип должен быстрее всего обрабатываться, то переменная этого типа должна попасть в такую область хранения[/uquote]
нет, это просто тип, без storage class-а.
Reflector писал(а):компилятор по сути один
не то чтобы :dont_know:
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="arkhnchul",url="/forum/viewtopic.php?p=3269267#p3269267"]не то чтобы :dont_know:[/uquote]
Я пробовал компилить gcc небольшие примеры сишного кода и если их компилить как С++, то часто получался точно такой же бинарник.

[uquote="Z_h_e",url="/forum/viewtopic.php?p=3269265#p3269265"]Что такое CCM я не знаю, однако я тоже когда то считал что медленнее, приводил доводы по этому поводу и провел эксперимент, который мои доводы и опроверг. Конечно эксперимент был проведен на определенном камне (т.е. нельзя говорить за все), но скорость обуславливается шинами и скоростью памяти. Шины для конкретного ARM одни и те же, не важно в каком он камне, а флеш однозначно медленнее ОЗУ,[/uquote]
Ок, проведем эксперимент:

Код: Выделить всё

volatile uint64_t a = -1;

for (int i = 0; i < 20; i++)
{
	a /= 7;
}
F429, работающий на 300MHz (6WS) выдает 2801 тактов при работе из флеша и 3440, для RAM, у которой никаких WS нет...
Для F103, работающего на 72MHz, получается 3516 vs 3627, все равно из флеша быстрее выполняется, даже без ART Accelerator.
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Сообщение uk8amk »

Всем спасибо за высказывание мыслей.
Перечитав ответы, подумал ещё раз, пересмотрел код и пришёл к следующему итогу:
1. Действительно, первый вариант кода моего примера проще к восприятию.
3. Перечитал статьи в интернете, кое-что вспомнилось про квалификатор volatile.
2. В настоящий момент нет острой необходимости к ускорению вычислений. На тактовой 24МГц прерывание может "съесть" до 30мкс. При текущих оборотах энкодера это вполне допустимо(есть хороший запас). Если обороты возрастут и задержка станет недопустмой, то перейду на проц F103 72МГц. Также может освободится ресурс если пересмотреть сам алгоритм вычислений вместо попытки вручную оптимизировать переменные volatile.

Теперь ответы на вопрос почему много вычислений происходит в прерываниях:
1. Сигнал с энкодера имеет не совсем обычный вид(см рис.). Поэтому аппаратный интрефейс обычного энкодера не подходит.
2. Энкодер генерирует мало импульсов на оборот. Для отслеживания позиции используется экстраполяция с учётом ускорений диска. Ожидаемый интервал корректируется программой с каждым новым импульсом.
Вложения
enc_signal.PNG
(54.95 КБ) 338 скачиваний
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

Reflector писал(а):Ок, проведем эксперимент:
Чтобы не оффтопить, создал новый топик по этому поводу.
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18787
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

arkhnchul писал(а):нет, это просто тип, без storage class-а
для чего он введен? я так полагаю, что для тех платформ, где есть разные storage classes, с разным временем доступа, компилятор должен стремиться (очевидно, в зависимости от параметров оптимизации?) подбирать для переменных этих типов наиболее быстрый класс хранения... ведь auto - это значит "на усмотрение компилятора" в зависимости от контекста... вот как бы fast-типы это дополнительная подсказка компилятору. я не прав в своих предположениях?
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Ответить

Вернуться в «Разные вопросы по МК»