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

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18647
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

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

Сообщение ARV »

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

Мой уютный бложик... заходите!
Реклама
jcxz
Мудрый кот
Сообщения: 1731
Зарегистрирован: Вт авг 15, 2017 10:51:13

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

Сообщение jcxz »

[uquote="Apparatchik",url="/forum/viewtopic.php?p=3265930#p3265930"]Я Вашу мысль понял, но там есче нужно после сдвига сделать вот это[/uquote]
Разбейте массив current[SIZE] на две части current[2][SIZE/2], заведите доп. переменную индекса i1 и
после каждой итерации меняйте части массива местами: i1 ^= 1;
Тогда не надо будет ничего "двигать".

Добавлено after 4 minutes 1 second:
[uquote="ARV",url="/forum/viewtopic.php?p=3265921#p3265921"]if(++index_I >= 400) index_I = 0;[/uquote]
Иногда лучше сделать 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 раза.
Реклама
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18647
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

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

Сообщение ARV »

jcxz писал(а):такое управление индексом добавит кучу лишних тактов в цикл. Много больше чем memcpy() на больших выровненных массивах.
только вы забываете, что у автора обработка в цикле ПОСЛЕ memcpy, т.е. все такты копирования пойдут плюсом к обработке. а управление индексом добавить по 2-3 такта на итерацию ПОЛЕЗНОГО цикла. так что это еще большой вопрос, что окажется быстрее по совокупности
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
jcxz
Мудрый кот
Сообщения: 1731
Зарегистрирован: Вт авг 15, 2017 10:51:13

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

Сообщение jcxz »

[uquote="ARV",url="/forum/viewtopic.php?p=3266075#p3266075"]
jcxz писал(а):такое управление индексом добавит кучу лишних тактов в цикл. Много больше чем memcpy() на больших выровненных массивах.
только вы забываете, что у автора обработка в цикле ПОСЛЕ memcpy, т.е. все такты копирования пойдут плюсом к обработке. а управление индексом добавить по 2-3 такта на итерацию ПОЛЕЗНОГО цикла. так что это еще большой вопрос, что окажется быстрее по совокупности[/uquote]
А вы компилировать пробовали, то что предложили? Где там 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().
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18647
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

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

Сообщение ARV »

jcxz писал(а):А вы компилировать пробовали, то что предложили?
нет, конечно.
jcxz писал(а):Кроме того данные у автора 16-битные, а memcpy() внутри оптимизирована и на больших массивах, выровненных на границу 32бит, будет копировать по 8 шт. 32-битных слов за итерацию, т.е. - будет тратить всего ~ 2 такта на копирование каждой пары значений.
собственно, с этого я и начал, так что не надо мне об этом рассказывать.

а все наши с вами рассуждения могут ничего не стоить, если компилятор хорошо оптимизирует.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Реклама
Аватара пользователя
Apparatchik
Держит паяльник хвостом
Сообщения: 908
Зарегистрирован: Вс май 23, 2010 13:55:42
Откуда: Украина, Александрия

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

Сообщение Apparatchik »

[uquote="jcxz",url="/forum/viewtopic.php?p=3266089#p3266089"]А если ещё как я писал, кратно увеличить размеры массивов, то во столько же крат можно уменьшить количество вызовов memcpy().[/uquote]
Этого сделать нельзя так, как в зависимости от частоты количество измерений на период разное и в идиале составляет 400шт, но постоянно плавает.
«И всё-таки она вертится!»
Реклама
jcxz
Мудрый кот
Сообщения: 1731
Зарегистрирован: Вт авг 15, 2017 10:51:13

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

Сообщение jcxz »

[uquote="ARV",url="/forum/viewtopic.php?p=3266096#p3266096"]а все наши с вами рассуждения могут ничего не стоить, если компилятор хорошо оптимизирует.[/uquote]
По-любому лучшая оптимизация индексных обращений к массивам - замена и на обращения через указатели, вычисленные перед циклом.
А в теле цикла - работа с этими указателями командами с автоинкрементом.
Но если есть модификация индексов внутри цикла, то заменить на указатели компилятор не сможет. А значит на каждой итерации будет заново считать исполнительные адреса для каждого операнда.
Вот во что компилируется приведённый мной код:

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

  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);
Быстрее почти некуда:

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

MOVS     R0,#+200
??DtePwm_0:
LDR      R3,[R2, #+400]
LDR      R6,[R2], #+4
SUBS     R0,R0,#+1
SMLAD    R1,R6,R3,R1
BNE.N    ??DtePwm_0
Всего 6 тактов на каждые 2 пары операндов (не считая условного перехода).

Добавлено after 4 minutes 9 seconds:
[uquote="Apparatchik",url="/forum/viewtopic.php?p=3266102#p3266102"]Этого сделать нельзя так, как в зависимости от частоты количество измерений на период разное и в идиале составляет 400шт, но постоянно плавает.[/uquote]
Ну и что?
Создаёте массив скажем примерно в 10 раз больше = 4000 значений. А это значит, что если в кольцевой буфер размером 400 значений каждый раз добавлять по примерно 400 новых значений, то значит примерно на одно добавление будет примерно одно пересечение границы. А если размер == 4000 значений, то значит пересечение будет примерно на каждое 10-е добавление.
А делать memcpy() нужно только при пересечении границы массива.
Так что без разницы - что фиксированное кол-во добавлять, что переменное.
Аватара пользователя
КРАМ
Друг Кота
Сообщения: 25362
Зарегистрирован: Чт янв 10, 2008 22:01:02
Откуда: Московская область, Фрязино

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

Сообщение КРАМ »

[uquote="Apparatchik",url="/forum/viewtopic.php?p=3265900#p3265900"]В общем зачем эти подробности? Я же сказал что уже работает и просто спросил может можно сдвинуть как то быстрее. Нет так нет.[/uquote]
Ну хотя бы затем, что порой не слишком, ээээ, оптимальный метод измерений дает раздутые никчемные расчеты. А порой и избыточные исходные данные.
uk8amk
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

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

Сообщение 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 );
 };	
}
Кто что думает на этот счёт?
arkhnchul
Друг Кота
Сообщения: 3092
Зарегистрирован: Пн апр 06, 2015 11:01:53
Откуда: москва, уфа

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

Сообщение arkhnchul »

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

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

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

Сообщение 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 процессоры, в которых моя задача решена на аппаратном уровне. Но мне они пока недоступны.
Аватара пользователя
Oxford
Опытный кот
Сообщения: 819
Зарегистрирован: Вт окт 23, 2012 13:17:25
Откуда: Прокопьевск
Контактная информация:

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

Сообщение Oxford »

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

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

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

Сообщение 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.
Reflector
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

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

Сообщение Reflector »

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

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

Сообщение arkhnchul »

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

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

Сообщение ARV »

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

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

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

Сообщение Reflector »

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

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

Сообщение arkhnchul »

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

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

Сообщение ARV »

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

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

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

Сообщение 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 может до сих пор что-то значит.
Ответить

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