|
|
  |
Странное предупреждение |
|
|
|
Jun 5 2018, 05:52
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Есть такая строчка в коде Код for (i=0; i < size_to_take; i++) { DataBuffer[i] = (data_out[j++]<<8) | data_out[j++]; } Выскакивает предупреждение Цитата Warning[Pa079]: undefined behavior: variable "j" (declared at line 176) (or a value reached by some form of indirection through it) is modified more than once without an intervening В GCC компайлере такого предупреждения нет.Что надо переделать чтоб предупреждение исчезло?
|
|
|
|
|
Jun 5 2018, 05:58
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Цитата(scifi @ Jun 5 2018, 11:54)  j++ два раза в одном выражении - это бяка. Результат выражения не определён. Не надо так делать. у меня тут выбора особо нету  если только так Код DataBuffer[i] = (data_out[j++]<<8); DataBuffer[i] |= data_out[j++]; по моему так еще уродливей
Сообщение отредактировал Jenya7 - Jun 5 2018, 06:00
|
|
|
|
|
Jun 5 2018, 06:18
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Цитата(scifi @ Jun 5 2018, 08:54)  j++ два раза в одном выражении - это бяка. Результат выражения не определён. Не надо так делать. Не результат выражения, а порядок операций. а делать так действительно не надо. Чем меньше неоднозначностей в коде, тем лучше. Ещё рекомендую статический анализатор запускать
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Jun 5 2018, 06:37
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(Jenya7 @ Jun 5 2018, 08:58)  по моему так еще уродливей Что мешает сделать так: Код DataBuffer[i] = (data_out[j] << 8) | data_out[j + 1]; j += 2; ??? И дело не в уродливости, а в корректности. Оригинальный код некорректен, слава яру и его предупреждениям. Цитата(MrYuran @ Jun 5 2018, 09:18)  Не результат выражения, а порядок операций. То есть вы утверждаете, что результат определён? Какое смелое заявление.
|
|
|
|
|
Jun 5 2018, 07:09
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(andrew_b @ Jun 5 2018, 09:49)  Рискну предположить, что стандарт не говорит о результате. Он говорит о порядке операций. Неверное предположение. Конечно, стандарт говорит о результате, иначе зачем он нужен? Результат складывается из последовательности операций, и если порядок операций не определён, то и результат не определён. Очевидно же! Цитата(MrYuran @ Jun 5 2018, 10:07)  В данном случае нет, но такое предупреждение бывает, например, при операциях с несколькими волатильными переменными Угу.
|
|
|
|
|
Jun 5 2018, 08:17
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Цитата(scifi @ Jun 5 2018, 12:37)  Что мешает сделать так: Код DataBuffer[i] = (data_out[j] << 8) | data_out[j + 1]; j += 2; ??? И дело не в уродливости, а в корректности. Оригинальный код некорректен, слава яру и его предупреждениям. То есть вы утверждаете, что результат определён? Какое смелое заявление. на этом варианте остановлюсь. спасибо.
|
|
|
|
|
Jun 5 2018, 13:24
|
Профессионал
    
Группа: Свой
Сообщений: 1 975
Регистрация: 30-12-04
Из: Воронеж
Пользователь №: 1 757

|
Цитата(scifi @ Jun 5 2018, 10:09)  Неверное предположение. Конечно, стандарт говорит о результате, иначе зачем он нужен? Цитата The grouping of operators and operands is indicated by the syntax.71) Except as specified later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified. Оно?
|
|
|
|
|
Jun 20 2018, 07:19
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(Jenya7 @ Jun 5 2018, 10:52)  Есть такая строчка в коде Код for (i=0; i < size_to_take; i++) { DataBuffer[i] = (data_out[j++]<<8) | data_out[j++]; } если нет перевёртывания с эндианами и DataBuffer - это 16-ти разрядное, то Код memcpy((void*)DataBuffer, (void*)&data_out[j], 2*size_to_take); j += 2*size_to_take; //если j дальше не нужно, удалить эту строку без ворнингов, безопаснее, и БЫСТРЕЕ. (явное преобразование к воид не обязательно).
|
|
|
|
|
Jun 20 2018, 10:24
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 20 2018, 13:10)  Bера в церкви. Вы как гуру должны не верить, а знать, что for-loop будет работать не быстрее, чем любые практические реализации memcpy()[/url]. Я и знаю, что это не так. И знаю почему. И я знаю как внутри работает memcpy(). Поэтому он будет быстрее только в определённых случаях. А в общем случае быстрее будет for loop. Особенно если вспомнить, что компиляторы умеют оптимизировать. Цитата(juvf @ Jun 20 2018, 13:10)  а про безопастность - так ТС стрельнул себе в ногу своим forech, причем даже где-то gcc не ругнулся. C memcpy не нужны манимуляции с j. Не знаю, что Вы имеете в виду под "безопасностью", но даже Вы в своём совете с memcpy() допустили пару неточностей.
|
|
|
|
|
Jun 20 2018, 10:37
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(jcxz @ Jun 20 2018, 15:24)  но даже Вы в своём совете с memcpy() допустили пару неточностей.  какую? Цитата Поэтому он будет быстрее только в определённых случаях. в каких? поделитесь?
|
|
|
|
|
Jun 20 2018, 17:59
|
Местный
  
Группа: Свой
Сообщений: 475
Регистрация: 14-04-05
Из: Москва
Пользователь №: 4 140

|
Цитата(scifi @ Jun 20 2018, 15:52)  Интересно, куда все торопятся? Если я знаю несколько способов решить одну и ту же задачу, то естественно выберу тот который работает быстрее и/или занимает меньше ресурсов. Иначе будет как с "640K ought to be enough for anybody". Цитата(scifi @ Jun 20 2018, 15:52)  Если судить по моему опыту в ымбеддед, ставить рекорды скорости приходится крайне редко  Именно потому что опыт это и есть набор эффективных реализаций, которые не тормозят там где не надо.
|
|
|
|
|
Jun 20 2018, 18:30
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 20 2018, 13:37)  какую? Причём вообще в данном примере memcpy()? ...если ТС судя по всему пытается поменять байты местами при перемещении из источника в приёмник? С чего Вы вообще взяли, что DataBuffer имеет размерность 16 бит, а data_out - 8 бит? Причём там вообще memcpy()??? Научитесь читать исходники! Цитата(scifi @ Jun 20 2018, 15:52)  Интересно, куда все торопятся? Если судить по моему опыту в ымбеддед, ставить рекорды скорости приходится крайне редко  И экономить электроэнергию - тоже редко?
|
|
|
|
|
Jun 20 2018, 18:42
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(jcxz @ Jun 20 2018, 21:30)  С чего Вы вообще взяли, что DataBuffer имеет размерность 16 бит, а data_out - 8 бит? Это очевидно из строчки DataBuffer[i] = (data_out[j++]<<8) | data_out[j++]; Если data_out будет больше 8 бит, то операция | будет перемешивать биты из двух соседних значений. (data_out[j++]<<8) | data_out[j++] имеет в этом случае не более 16 значащих бит. Соответственно DataBuffer не менее 16 бит. Какой смысл делать больше?
|
|
|
|
|
Jun 20 2018, 19:31
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(adnega @ Jun 20 2018, 21:42)  Это очевидно из строчки DataBuffer[i] = (data_out[j++]<<8) | data_out[j++]; Уважаемый, из этой строчки очевидно только, что к некоему объекту DataBuffer применяются операции индексирования и присвоения значения, а к объекту data_out - операции индексирования и операции приведения к другому типу (возможно целочисленному). Всё остальное - Ваши фантазии, не более. Цитата(adnega @ Jun 20 2018, 21:42)  Если data_out будет больше 8 бит, то операция | будет перемешивать биты из двух соседних значений. И что? Может это и есть цель ТСа? Цитата(adnega @ Jun 20 2018, 21:42)  Соответственно DataBuffer не менее 16 бит. Какой смысл делать больше? Я видимо ошибся форумом... Думал - тут форум программистов и электронщиков, а оказывается - гадалок и экстрасенсов. Я, как можете заметить, не пытаюсь домысливать тайные намерения автора. И я понятия не имею - чего же он пытается изобразить. Я просто основываюсь на том, что вижу в вопросе. И всех возможных в мире задач я тоже не знаю - мало ли чего человек хочет? Может ему требуется читать из источника пары байт и записывать их по адресу назначения с расширением до 32-бит. Не задумывались? Опять спросите "зачем"? Ну хотя бы например: 1. Многие МК имеют SPI-контроллеры, у которых передаваемое данное записывается в регистр, младшие 16 бит которого - собственно передаваемые биты, а старшие биты - различные биты управления (управление сигналами CS, управление межсловным интервалом и т.п.). Знаю несколько таких МК. Вот если нужно скажем передавать байты в SPI в том порядке, в котором они лежат в памяти, но передавать 16-битными словами (для уменьшения блока пересылки DMA) и при этом к словам добавить управляющую информацию, то может и потребоваться перевёртывание 16-битных слов с расширением их до 32 бит. 2. Или например - он готовит фрейм для передачи в видеопамять LCD-контроллера (опять-же по 16-битному SPI), который (контроллер) принимает пиксели с глубиной цвета 32 бита, а в памяти автора отрисовка идёт 16-битными пикселями. Соответственно - при передаче необходимо расширение каждого пикселя до 32 бит. 3. Или: у автора в устройстве на SPI висит пара слэйвов, соединённых в daisy-chain. Каждый из них имеет размер слова ==16 бит. И он хочет записывать в дальний в цепочке слэйв слова из data_out, а в ближний - нули. Ну скажем эти два слэйва - два 16-разрядных ЦАП, соединённых в daisy-chain. И его DMA должен по неким запросам выдавать по 32 бита на запрос (по слову в каждый ЦАП). Вот он и формирует буфер для DMA таким образом. И это - всего три из тысяч возможных вариантов!
|
|
|
|
|
Jun 21 2018, 04:24
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(Obam @ Jun 20 2018, 23:49)  GCC не ругнётся нигде и никогда: у ТСа IAR  2 Obam, читаем ещё раз первый пост ТС до конца Цитата В GCC компайлере такого предупреждения нет. Цитата Причём вообще в данном примере memcpy()? ...если ТС судя по всему пытается поменять байты местами при перемещении из источника в приёмник? С чего Вы вообще взяли, что DataBuffer имеет размерность 16 бит, а data_out - 8 бит? Причём там вообще memcpy()??? Научитесь читать исходники! adnega - ППКС, 2jcxz - из кода видно, что с вероятностью близкой к 100, данные из какого-то байтового тх/рх буфера перекидываются в нужный массив DataBuffer. Научитесь читать исходники!. Но не факт что это так, поэтому я сразу написал Цитата если нет перевёртывания с эндианами и DataBuffer - это 16-ти разрядное, то 2jcxz - Научитесь читать посты участников. Цитата мало ли чего человек хочет? Может ему требуется читать из источника пары байт и записывать их по адресу назначения с расширением до 32-бит. Не задумывались? Может быть! Согласен. Может data_out - это класс, а операторы << и | перегруженны. Может даже оператор "++" перегружен в "--". Может где то false переопределён в true. Но из кода видно, что с большей вероятностью просто перекачка из 8 бит массива в 16 битный, поэтому я и сделал оговорку. Что касается memcpy() и остального.... Цитата И я знаю как внутри работает memcpy() Как вы можете знать, как работает memcpy() у ТС, если memcpy реализуется разработчиками каждого компилятора под каждую архитектуру? Вы знаете как каждый memcpy() реализован? У каждого своя реализация. Как правило, memcpy() реализован так, что из всех возможных реализаций код memcpy будет самый оптимальный, вплоть до ухода в асм. Если пользователь компилятора/библиотеки и решит написать свой копипаст, то он будет менее эффективный, в лучшем случае будет такойже, поэтому при копировании памяти не нужно замарачиваться и изобретать скоростной велосипед, а просто использовать memcpy(). Наверно бывают случаю, что memcpy сырой, написан индусами и можно свой написать оптимальней, но это на столько редко.... и нужно хорошо знать архитектуру. Потратите много времени, выиграете 1-2 такта на копировании 1 байта.более того, если ЕСЛИ всё же нет перевертывания и это 8 и 16 бит, то даже плохой memcpy будет быстрее кода ТС. Посмотрите сколько лишних операций в for у ТС! какие-то приведения типов, операторы, << и |, дополнительный j, операции j++!!! УЖАС!!!(кстати.... j++ достаточно медленный, по сравнению с ++j). Более того, если ЕСЛИ data_out - это signed char или int8_t, то в коде ТС ошибка, которая обнаружиться только при выполнении, и хорошо если на столе ТС, а может и через год-два у пользователя. jcxz я сюда не батлится с вами захожу, я тут черпаю чужой опыт, делюсь своим и чужим опытом. Поделитесь вы..... Цитата Поэтому он будет быстрее только в определённых случаях. В каких случаях? Цитата Вы в своём совете с memcpy() допустили пару неточностей. какие неточности? Опять же, mне на батл пофиг, просто я не хочу ошибаться и других вводить в заблуждение в отличие от некоторых Или вы пустослов? Тогда можно нужно отфильтровать ваши посты, ибо это всё равно что "на заборе написано". ps вы сами себе противоречите, причем сразу в одном посте..... Цитата ТС судя по всему пытается поменять байты местами при перемещении из источника в приёмник... С чего Вы вообще взяли, что DataBuffer имеет размерность 16 бит, а data_out - 8 бит? так всё таки "ТС ... пытается поменять байты" или "С чего Вы вообще взяли, что ... data_out - 8 бит?" С чего вы решили, что я утверждаю, что DataBuffer 16 бит, а data_out - 8 бит? Я писал ЕСЛИ, вы знаете значение слова "если"?
|
|
|
|
|
Jun 21 2018, 05:32
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(juvf @ Jun 21 2018, 07:24)  С чего вы решили, что я утверждаю, что DataBuffer 16 бит, а data_out - 8 бит? Я писал ЕСЛИ, вы знаете значение слова "если"? Среди профессионалов принято так: если есть наиболее вероятное и простое объяснение, то нужно пользоваться именно им. В противном случае один мой знакомый говорил: "а если инопланетяне прилетели и сделали {подставить нужное}". Если ТС-инопланетянин, пишет, вроде, по нашему, но по ихнему это совсем другой смысл, то jcxz должен посыпать голову пеплом раз не предположил, что такое возможно. Дальше смысл беседы в этом направлении теряется. Если по исходнику понятно, что источник шире 8 байт приведет к перемешиванию бит, а в приемнике в итоге получается только 16 значащих бит, то с уверенностью можно принять за факт их размеры 8 и 16 бит соответственно, и при необходимости заявлять "сам дурак", если ТС начнет добавлять противоречащие новые обстоятельства. В соседней ветке про таймеры ТС на третей станице пошел по второму кругу, хотя у меня была полная уверенность, что точка в вопросе поставлена. В этой связи, jcxz поддерживаю, т.к. без телепатии, а на одной только логике, не понять, что нужно ТС.
|
|
|
|
|
Jun 21 2018, 07:05
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 21 2018, 07:24)  Как вы можете знать, как работает memcpy() у ТС, если memcpy реализуется разработчиками каждого компилятора под каждую архитектуру? Вы знаете как каждый memcpy() реализован? У каждого своя реализация. "из кода видно, что с вероятностью близкой к 100..." где-то я это уже слышал недавно? Так вот - у ТС-а IAR, и я им пользуюсь; у ТС-а (согласно строки выше) Cortex-M - и я под него сейчас отлаживаю. Аргументов достаточно? Цитата(juvf @ Jun 21 2018, 07:24)  Как правило, memcpy() реализован так, что из всех возможных реализаций код memcpy будет самый оптимальный, вплоть до ухода в асм. Если пользователь компилятора/библиотеки и решит написать свой копипаст, то он будет менее эффективный, в лучшем случае будет такойже, поэтому при копировании памяти не нужно замарачиваться и изобретать скоростной велосипед, а просто использовать memcpy(). С этим категорически не согласен! Попробуйте когда-нить не теоретизировать внустую, а скомпилировать и посмотреть разные варианты с копированием в цикле на разных компиляторах с включённой полной оптимизацией по скорости и с разными условиями цикла (короткий цикл/длинный, условия окончания и указатели - переменные или известны на этапе компиляции). И будете удивлены. Вариант с memcpy() на коротких циклах, где кол-во проходов и/или указатели заданы переменными, проиграет варианту с простым for, так как ему не надо: a) сохранять содержимое из scratch регистров; б) делать вызов memcpy() и возврат; в) внутри memcpy() выполнять ветвление выбирая нужный вариант цикла копирования по условию длины цикла, выравниваний первого и второго аргумента и т.п. На циклах с заранее (на этапе компиляции) известными условиями выполнения, for-вариант опять же будет как минимум не хуже, просто хотя-бы потому что компилятор зная условия выполнения цикла может подставить нужную последовательность команд (подобную оптимальному копированию внутри memcpy()), но с другими более удобными регистрами и их количеством - удобными для данной точки кода. А некоторые компиляторы, так и вообще цикл for так оптимизируют (при определённых условиях) что готовому memcpy() и не снилось - в разы быстрее по скорости. Смотрим на компиляторы для DSP-ядер. Цитата(juvf @ Jun 21 2018, 07:24)  Посмотрите сколько лишних операций в for у ТС! какие-то приведения типов, операторы, << и |, дополнительный j, операции j++!!! УЖАС!!! И что? Современные оптимизаторы творят чудеса. И им по-барабану Ваши жалкие потуги заменить операции индексирования на указатели и подобное - они это и сами хорошо делают, даже ещё и лучше. Так что если бы код автора не был такой кривой, результат работы оптимизирующего компилятора мог быть лучше memcpy(). Я в своё время, разрабатывая для DSP, немало заменил копирований через memcpy() на циклы for - это давало внушительный прирост скорости. Цитата(juvf @ Jun 21 2018, 07:24)  (кстати.... j++ достаточно медленный, по сравнению с ++j). Опять чушь! Если предположить, что автор использует ARM, то что пре- что пост-инкремент - без разницы, и то и другое на ARM можно сделать в пределах одной команды выборки из памяти. Откройте мануал по системе команд и найдите разницу между: LDRB Rx, [Ry], #1 и LDRB Rx, [Ry, #1]!Цитата(juvf @ Jun 21 2018, 07:24)  В каких случаях? какие неточности? Во-первых: memcpy там не к месту как я писал выше; во-вторых: ничего не известно о значении size_to_take, а если она перед началом цикла может быть отрицательной (и i - тоже знаковое), что будет с вашим вариантом на memcpy()? в-3-х: с чего Вы приводите входные аргументы к типу void *? А если у ТС-а DataBuffer и data_out объявлены к примеру с модификатором volatile, т.е. указывают на strong ordered memory? Например DataBuffer или data_out - это FIFO-буфера в некоей периферии, чувствительные к размерности и последовательности операций записи/чтения. Я уж не говорю о том, что эта память может позволять только доступы определённой разрядности... Цитата(juvf @ Jun 21 2018, 07:24)  вы сами себе противоречите, причем сразу в одном посте..... В чём именно противоречие?  Цитата(adnega @ Jun 21 2018, 08:32)  Если по исходнику понятно, что источник шире 8 байт приведет к перемешиванию бит, а в приемнике в итоге получается только 16 значащих бит, то с уверенностью можно принять за факт их размеры 8 и 16 бит соответственно, и при необходимости заявлять "сам дурак", Из "исходника" даже не ясно, что именно нужно этому горе-программисту (если data_out указывает на unsigned char) - поменять местами байты при копировании или может он хочет продублировать байты. Т.е. сделать так: 1. *(u16 *)&DataBuffer[i] = __REVSH(*(u16 __packed *)&data_out[j]); j += 2;или так: 2. *(u16 *)&DataBuffer[i] = (uint)*(u8 *)&data_out[j++] * 0x101u;а может и что другое.....
|
|
|
|
|
Jun 21 2018, 07:27
|
Местный
  
Группа: Свой
Сообщений: 475
Регистрация: 14-04-05
Из: Москва
Пользователь №: 4 140

|
Цитата(Сергей Борщ @ Jun 21 2018, 09:22)  К сожалению, аборигены запомнили только, что нужно хлопать при приземлении. Продвинутые еще впп научились строить, чтобы их посещали.
|
|
|
|
|
Jun 21 2018, 07:50
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
ну на конец то предметный разговор, а не "слепая вера" Цитата Попробуйте когда-нить попробую обязательно. Цитата Современные оптимизаторы творят чудеса. И им по-барабану Ваши жалкие потуги заменить операции индексирования на указатели и подобное - они это и сами хорошо делают, даже ещё и лучше. Это я слышал и/или знаю.... Но, во первых, современные оптимизаторы иногда портят код так, что он не работает как надо, приходится либо отключать оптимизацию полностью, либо на отдельной функции. Во вторых про потуги вы зря.... когда мой код перестал влазить в флешь, а в прерываниях стал долго задерживаться, то сделал рефакторинг - код и влез и стал быстрее работать. Одна строчка до рефакторинга была 6 машинных команд, стала 2 или 4. Убрал всякие лишние << |, на каждом участке экономил где по 10-20 байт флеша, где по 100. Цитата во-вторых: ничего не известно о значении size_to_take, а если она перед началом цикла может быть отрицательной (и i - тоже знаковое) Морите!? О чем тут дискутировать, если вы не знаете как работает for? Если size_to_take отрицательная и i тоже знаковое, то в примере ТС в тело цикла for не разу не зайдем. Это вообще букварь Си. Цитата с чего Вы приводите входные аргументы к типу void *? Да, согласен, неточность есть. Если быть скрупулёзным к явным преобразованиям, то нужно так memcpy((void*)DataBuffer, (const void*)&data_out[j], 2*size_to_take); Я не утверждаю, я всего лишь предположил, что копировать память лучше через memcpy. Если это не 8 и 16 бит, то забыли про мой совет. О чем спор? Цитата А если у ТС-а DataBuffer и data_out объявлены к примеру с модификатором volatile И что? в чем разница вот в таких кодах с volatile (архитектура ст первый, без перевёртывания): Вариант 1: Код volatile uint16_t DataBuffer[100]; volatile uint8_t data_out [1024]; uint16_t size_to_take = 100; uint8_t j = 17; for(uint16_t i=0; i<size_to_take; i++ ) { DataBuffer[i] = (data_out[j] << 8)| data_out[j+1]; j += 2; } Вариант 2: Код volatile uint16_t DataBuffer[100]; volatile uint8_t data_out [1024]; uint16_t size_to_take = 100; memcpy((void*)DataBuffer, (const void*)&data_out[17], 2*size_to_take); Или по вашему 1-ый вариант работать будет, а второй нет?
|
|
|
|
|
Jun 21 2018, 08:41
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 21 2018, 10:50)  Морите!? О чем тут дискутировать, если вы не знаете как работает for? Если size_to_take отрицательная и i тоже знаковое, то в примере ТС в тело цикла for не разу не зайдем. Это вообще букварь Си. и....? Продолжаем думать дальше. Подсказываю ещё раз: А что будет с Вашим вариантом на memcpy()? Цитата(juvf @ Jun 21 2018, 10:50)  И что? в чем разница вот в таких кодах с volatile (архитектура ст первый, без перевёртывания): Или по вашему 1-ый вариант работать будет, а второй нет? В каких-то случаях - да. Читайте мои посты внимательнее. Мы не знаем в какой памяти находятся исходный и целевой адреса. Возможно они находятся в памяти, требующей строго определённого порядка доступа и определённой разрядности доступа (strong ordered memory). Или в памяти при доступе к которой происходят прочие события (например FIFO-буфера периферии (SPI например) при записи/чтении данных в/из них производят другие действия: обмен с регистрами сдвига, удаление данных из FIFO и т.д.; а также чувствительны к разрядности операций чтения/записи - при доступе недопустимой разрядности получите fault; и чувствительны к выравниванию (хотя основная память МК может допуcкать невыровненный доступ, но некоторые регионы - могут генерить fault при невыровненном доступе)). А какие именно операции доступа к памяти будут использованы внутри memcpy() - нет никакой гарантии. Если например DataBuffer указывает на 16-разрядный SPI-FIFO и size_to_take>=2, то внутри memcpy() может объединить доступы в 32-разрядные и в результате получим bus-fault. Так что бездумно использовать memcpy() нельзя.
|
|
|
|
|
Jun 21 2018, 08:44
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 21 2018, 10:50)  Но, во первых, современные оптимизаторы иногда портят код так, что он не работает как надо, приходится либо отключать оптимизацию полностью А вот здесь: код в студию!Ибо в 99.9% таких случаев причина - баг в коде. Я даже скажу больше: я при отладке кода проверяю его работу с включенной и отключенной оптимизацией - это помогает находить скрытые баги в коде.
|
|
|
|
|
Jun 21 2018, 09:01
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата Продолжаем думать дальше. Подсказываю ещё раз: А что будет с Вашим вариантом на memcpy()? Что за глупость? при чем тут memcpy с отрицатеольными size_to_take? В коде ТС явно size_to_take > 0. Цитата(jcxz @ Jun 21 2018, 13:44)  А вот здесь: код в студию! Код #pragma optimize=none unsigned int crc16_byte(unsigned int crc, unsigned int data) { //const unsigned int Poly16=0xA001; unsigned int LSB; crc = ((crc^data) | 0xFF00) & (crc | 0x00FF); for (uint8_t i=0; i<8; i++) { LSB=(crc & 0x0001); crc >>= 1; if(LSB) crc=crc^0xA001; } return crc; } Такой код работает, если убрать прагму, то црц считается не правильно. Для фантазёров, видящих за рамками кода всякое ООП, перегрузку операторов, переопределение типов и прочую фуе ту.... перегрузки операторов нет, переопределений типов и чего либо ещё нет. тип unsigned int 16 бит!
|
|
|
|
|
Jun 21 2018, 09:06
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(jcxz @ Jun 21 2018, 11:44)  Ибо в 99.9% таких случаев причина - баг в коде.  Да, но 0.1% все же баг в компиляторе. У меня есть проект, который собирается и работает при O2 и не собирается при Os. Интересно? Код C:\Users\user\AppData\Local\Temp\ccShzzAY.s: Assembler messages: C:\Users\user\AppData\Local\Temp\ccShzzAY.s:3151: Error: value of 256 too large for field of 1 bytes at 000000000000103a make.EXE: *** [obj_sw/esp8266.o] Error 1 В asm Код .L276: .byte (.L274-.L276)/2 - переполнение Меняешь две строчки в Си-исходнике и проект начинает собираться.
|
|
|
|
|
Jun 21 2018, 09:07
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(juvf @ Jun 21 2018, 12:01)  Такой код работает, если убрать прагму, то црц считается не правильно. Для фантазёров, видящих за рамками кода всякое ООП, перегрузку операторов, переопределение типов и прочую фуе ту.... перегрузки операторов нет, переопределений типов и чего либо ещё нет. тип unsigned int 16 бит! Не видно в этом коде никакого криминала. Конечно, нельзя исключить того, что криминал возникает вне этой функции (она же не в вакууме существует). Опять же, не исключён глюк компилятора. У меня яр для coldfire неправильно считал MD5 при включенной оптимизации. Поймать его за руку на конретной инструкции сложно, так как код MD5 зубодробительный.
|
|
|
|
|
Jun 21 2018, 10:18
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 21 2018, 12:01)  Что за глупость? при чем тут memcpy с отрицатеольными size_to_take? В коде ТС явно size_to_take > 0. Где это "явно указано"? Ни объявления ни присвоения значения size_to_take там нету. А это означает, что size_to_take к началу цикла может принимать любое значение. И алгоритм вполне может быть рассчитан на то, что при size_to_take<=0 не должно выполняться ни одной итерации. Глупость - это видеть то, чего нет. Цитата(juvf @ Jun 21 2018, 12:01)  Такой код работает, если убрать прагму, то црц считается не правильно. В исходнике вроде криминала нет. Но это ничего не доказывает. Без анализа ассемблерного результата ничего сказать нельзя. Да даже анализ ассемблерного варианта может ничего не дать. Элементарная причина "неверной работы оптимизированного варианта" при работе оптимизированного например: переполнение стека в вызывающей задаче. Оптимизированные варианты, как правило расходуют больше стека (больше регистров используют, соответственно - больше их сохраняют на входе в функции; заменяют некоторые повторяющиеся куски кода на дополнительные подпрограммы, что опять же ведёт к увеличению расхода стека и т.п.), а в неоптимизированном варианте стека ещё хватает. Другой вариант: при вкл. оптимизации функция может инлайниться в точки вызова, вызывая перераспределение регистров (увеличение их использования), что может приводить к проявлению скрытых багов в вызывающих функциях, которые при меньшем кол-ве используемых регистров не проявляются. Так что наиболее вероятный диагноз остаётся тем же: с вероятностью 99.9% - баги в коде.  И необязательно что в этой процедуре. Цитата(adnega @ Jun 21 2018, 12:06)  Да, но 0.1% все же баг в компиляторе. У меня есть проект, который собирается и работает при O2 и не собирается при Os. Интересно? В приведённом Вами примере баг в Вашем коде, а не в компиляторе. О чём компилятор Вам и сообщает. Вы вычисляете выражение, результат которого зависит от взаимного расположения меток. Их взаимное расположение видимо определяется на этапе компиляции (в зависимости от результата компиляции кода, который между ними - размер его меняется). Естественно - когда результат не влазит в байт, компилятор Вам об этом сообщает. В чём его ошибка? А писать так код - это выстрел себе в ногу: сегодня компилится, завтра - нет. Цитата(juvf @ Jun 21 2018, 12:24)  Я тоже про это думал. На весь проект стоит максимальная оптимизация по размеру кода. Дебажил на уровне Си, в асм углубляться не стал. Одна строчка #pragma optimize=none давала правильную работу расчета црц. Поэтому врят-ли что-то в остальном коде не так. Времени не было искать глюк в компиляторе или ещё где.... Видимо это был 0,1%. Ну да - обычное оправдание багописателей в таких случаях: "не было времени искать баг, поэтому я закопал его поглубже". Это ничего, что потом, через неопределённой время он опять проявится и вдарит по лбу в самом неожиданном месте. Когда совершенно безобидный код станет глючить. И опять виноват конечно будет компилятор, ну кто-ж ещё!  При отладке одно из главнейших правил: ПРИ ПРОЯВЛЕНИИ БАГА НЕ СЛЕДУЕТ СТАРАТЬСЯ ДОБИТЬСЯ ТОГО, ЧТОБЫ ОН НЕ ПРОЯВЛЯЛСЯ. НАДО НАОБОРОТ - ЗАКРЕПИТЬ УСЛОВИЯ, ПРИ КОТОРЫХ ОН ПРОЯВЛЯЕТСЯ, ДОБИТЬСЯ ЕГО ПОВТОРЯЕМОСТИ И ИСКАТЬ ЕГО ПРИЧИНУ И УСТРАНИТЬ! И пока этого не сделано - двигаться дальше нельзя. Ведь не тот баг страшен, который чётко проявляется, а тот - что проявляется иногда, внезапно и хаотично. Цитата(scifi @ Jun 21 2018, 12:07)  Поймать его за руку на конретной инструкции сложно, так как код MD5 зубодробительный. Один из вариантов действий в такой ситуации: Скомпилить подозреваемую функцию с оптимизацией. Взять ассемблерный результат её компиляции, оформить в виде асм-функции. И вставить в исходник вместо вызова си-функции. Выключить оптимизацию и проверить работу кода. Если теперь глюк появился и в неоптимизированном варианте, то только в этом случае можно предполагать, что проблема в данной оптимизированной функции. Далее - только анализ этой ассемблерной функции.
|
|
|
|
|
Jun 21 2018, 10:24
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(jcxz @ Jun 21 2018, 14:53)  Где это "явно указано"? Дальше в эту сторону дискутировать не выжу смысла. Учитесь читать исходный код.Цитата Так что наиболее вероятный диагноз остаётся тем же: с вероятностью 99.9% - баги в коде. Если не на кофейной гуще гадать, а на картах Таро, то баг в ОС, на которой кросскомпиляция идет. Вот ещё пример Код #define __IO volatile /*!< defines 'read / write' permissions */ typedef struct SPI_struct { __IO uint8_t SR; /*!< SPI status register */ } SPI_TypeDef; ... #define SPI1 ((SPI_TypeDef *) SPI1_BASE) SPI_FLAG_BSY = (uint8_t)0x80 ...
#pragma optimize=none //без прагмы waitSpi(); не дожидается выполнения inline void waitSpi() { while( (SPI1->SR & (uint8_t)SPI_FLAG_BSY));} Тут без прагмы вход в waitSpi и мгновенный выход, не зависимо от флага SPI_FLAG_BSY. Цитата переполнение стека в вызывающей задаче. ээээ.... а разве при переполнении стека программа аварийно не завершается? В моём случае с crc16_byte() идет подсчет црц, но на выходе он не вырный, падения программы нет. Програма продолжает работать, но бракует пакет по црц. Принятые данные не портятся во время работы crc16_byte() и во время работы программы в целом. Хотел подебажить crc16_byte(), но с оптимизацией код не шагается по строчками си. А без оптимизации шагается но и работает без ошибок. Цитата Да даже анализ ассемблерного варианта может ничего не дать. Элементарная причина "неверной работы оптимизированного варианта" при работе оптимизированного например: переполнение стека в вызывающей задаче. Что-то я не пойму.... Вот, вы же сами утверждаете, что оптимизатор может сломать работу программы написанной без ошибок. Я о том же.
|
|
|
|
|
Jun 21 2018, 10:25
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(jcxz @ Jun 21 2018, 13:09)  В приведённом Вами примере баг в Вашем коде, а не в компиляторе. О чём компилятор Вам и сообщает. Вот мой код. Это участок типа Код if(0){} else if(состояние_1){сделать_1;} else if(состояние_2){сделать_2;} Так не собирается. А так Код if(0){} else if(состояние_2){сделать_2;} else if(состояние_1){сделать_1;} нет проблем. Это чисто косяк компилятора. Вот полный код: CODE //------------------------------------------- // ESP8266_ST_UDP0_START //- - - - - - - - - - - - - - - - - - - - - - else if(esp8266_state == ESP8266_ST_UDP0_START) { if(esp8266_cmd_flag == ESP8266_CMD_START) { send_cmd("AT+CIPSTART=0,\"UDP\",\"255.255.255.255\",5002,5001,2"); } if(esp8266_to > 1000) { SEND_TO; esp8266_to_cmd(ESP8266_ST_IDLE); } } //------------------------------------------- // ESP8266_ST_UDP0_SEND //- - - - - - - - - - - - - - - - - - - - - - else if(esp8266_state == ESP8266_ST_UDP0_SEND) { if(esp8266_cmd_flag == ESP8266_CMD_START) { esp8266_cmd_flag = ESP8266_CMD_ST + 0; esp8266_to = 0; esp8266_last_cmd[0] = 0; bl_export.bl_sp_str(&esp8266, "AT+CIPSEND=0,"); bl_export.bl_sp_dec(&esp8266, esp8266_udp_send_len); bl_export.bl_sp_str(&esp8266, "\r\n"); bl_export.bl_sp_start(&esp8266); } if(esp8266_to > 1000) { SEND_TO; esp8266_to_cmd(ESP8266_ST_IDLE); } }
Если соответствующие блоки ESP8266_ST_UDP0_START и ESP8266_ST_UDP0_SEND в исходнике переставить местами, то компилятор генерит правильный s-файл. Иначе генерит таблицу, в которой смещения не помещаются. Где тут моя ошибка? Где ошибка компилятора я разобрался и на всякий случай сменил компилятор (была сборка arm-kgp-eabi-procyon).
|
|
|
|
|
Jun 21 2018, 10:50
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(jcxz @ Jun 21 2018, 15:18)  При отладке одно из главнейших правил: ПРИ ПРОЯВЛЕНИИ БАГА НЕ СЛЕДУЕТ СТАРАТЬСЯ ДОБИТЬСЯ ТОГО, ЧТОБЫ ОН НЕ ПРОЯВЛЯЛСЯ. НАДО НАОБОРОТ - ЗАКРЕПИТЬ УСЛОВИЯ, ПРИ КОТОРЫХ ОН ПРОЯВЛЯЕТСЯ, ДОБИТЬСЯ ЕГО ПОВТОРЯЕМОСТИ И ИСКАТЬ ЕГО ПРИЧИНУ И УСТРАНИТЬ! И пока этого не сделано - двигаться дальше нельзя. так я нашел причину и устранил. )) inline void waitSpi() { while( (SPI1->SR & (uint8_t)SPI_FLAG_BSY));} - вот этот код не работает с оптимизацией в пустом хороводе. И не изредка - а всегда. 100 раз из 100. А без оптимизации работает как часы. ни каких сбоев в обмене по SPI замечено не было. Цитата Ведь не тот баг страшен, который чётко проявляется, а тот - что проявляется иногда, внезапно и хаотично. У меня црц считается с ошибкой всегда, когда вкл оптимизация, не изредка, а всегда. И всегда считается правильно с отключенной оптимизацией. Нету хаотичных и внезапных сбоев.
|
|
|
|
|
Jun 21 2018, 11:06
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 21 2018, 13:24)  ээээ.... а разве при переполнении стека программа аварийно не завершается? При переполнении стека симптомы могут быть какие угодно: может fault вылететь, а могут просто данные испортиться. Цитата(juvf @ Jun 21 2018, 13:24)  А без оптимизации шагается но и работает без ошибок. Я уже писал выше что можно сделать. Скомпилить данную функцию с оптимизацией. А потом вырезать её ассемблерный листинг и вставить в код компилируемый без оптимизации (как асм-функцию). И проверить работу кода теперь. И по асму шагать не надо, надо выучить ассемблер и "глазами" аналитически сравнить си-исходник с асм-результатом. И только если в этом случае будут найдены несоответствия между ними - только в этом случае можно однозначно говорить о вине компилятора. Цитата(juvf @ Jun 21 2018, 13:24)  Что-то я не пойму.... Вот, вы же сами утверждаете, что оптимизатор может сломать работу программы написанной без ошибок. Я о том же. Если Вы считаете, что в недостаточном объёме выделенной под стек памяти виноват оптимизатор, а не "пейсатель" кода, то больше разговаривать не о чем.....
|
|
|
|
|
Jun 21 2018, 11:12
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(adnega @ Jun 21 2018, 13:25)  компилятор генерит правильный s-файл. Иначе генерит таблицу, в которой смещения не помещаются. Где тут моя ошибка? Где ошибка компилятора я разобрался и на всякий случай сменил компилятор (была сборка arm-kgp-eabi-procyon). Из Вашего описания я понял, что асм-файл писали Вы сами.... Для таких малопопулярных ядер как ESP8266, вполне допускаю что компилятор гораздо более сырой, чем для main stream Cortex-M например. Цитата(scifi @ Jun 21 2018, 14:09)  Советую взять современный GCC и дать ему ключики -Os -flto. При любом более-менее нетривиальном коде "аналитическое сравнение" легко способно перейти в бессонные ночи  У juvf функция очень тривиальная и маленькая. В худшем случае будет бессонный час.
|
|
|
|
|
Jun 21 2018, 11:19
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 21 2018, 13:50)  У меня црц считается с ошибкой всегда, когда вкл оптимизация, не изредка, а всегда. И всегда считается правильно с отключенной оптимизацией. Нету хаотичных и внезапных сбоев. Это не устранение причины. Это как вместо лечения причины болезни, бороться с её симптомами. Если Вы подозреваете, что виноват компилятор, то устранение бага в таком случае - замена компилятора. Ведь если он косячит в данной функции, то где гарантия что не косячит в других местах? Просто эти косяки сейчас не проявляются (например - по этим веткам ветвления не проходит управление, но при каком-то стечении обстоятельств в будущем оно пройдёт и баг получит уже пользователь). Но вину компилятора сперва нужно однозначно доказать.
|
|
|
|
|
Jun 21 2018, 11:19
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(jcxz @ Jun 21 2018, 16:06)  При переполнении стека симптомы могут быть какие угодно: может fault вылететь, а могут просто данные испортиться. я же писал, данные не портятся, в фолаут не вылетаю, а црц считает с ошибкой. Цитата Если Вы считаете, что в недостаточном объёме выделенной под стек памяти виноват оптимизатор, а не "пейсатель" кода, то больше разговаривать не о чем.....  Я достаточно выделил памяти на стек. Без оптимизации глубины стека хватает с запасом. Если оптимизатор удвоил запросы ОЗУ на стек, то мне такой оптимизатор не нужен. Во вторых, бывало вылазил за стек.... на разных проекта.... либо фолаут, либо данные портились. Это быстро обнаруживается. Тут идет подсчет црц, но неправильный. Ни чего не портиться и не падает.
|
|
|
|
|
Jun 21 2018, 11:22
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(scifi @ Jun 21 2018, 14:13)  Пожалуйста, похожая история с Cortex-M: тыц. Да я не спорю, что и в компиляторах есть косяки. Я даже сам несколько лет назад выкладывал сюда на форум описание одного из них. Но я чётко доказал, что это именно косяк компилятора (это был IAR вроде v6.20 или v6.40). И выложил и фрагмент си-кода и результат компиляции (ассемблер) и ключи компиляции, при которых этот результат получен. Чтобы каждый смог лично проверить.
|
|
|
|
|
Jun 21 2018, 11:46
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(jcxz @ Jun 21 2018, 16:19)  Ведь если он косячит в данной функции, то где гарантия что не косячит в других местах? Нет гарантии. Все места проверяются глубоким тестированием. А если компилятор оптимизатор не косячит - то нет ни какой гарантии, что нет косяков пейсателя/компилятора в других местах. Все места проверяются глубоким тестированием, стрестестами и т.п. , Это касается любого компилятора/оптимизатора, любого кода. Цитата(scifi @ Jun 21 2018, 16:04)  Я так понимаю, версия про "volatile" была проверена и не подтвердилась? .... конечно первым делом volatile. Цитата SPI1->SR нужно начинать читать не сразу, а через пару тактов, без оптимизации задержка получалась сама собой - как вариант. Чуть не написал "Слона не увидел", но слона нет. Во первых в даташите про это ни чего не говориться, во вторых.... сначала готовлю данные, потом их отправляю в спи, не дожидаясь завершения ..... на русском сложно писать, написшу на си Код sleepMs(1000); for() { //подготовка данных waitSpi(); //запись данных в спи регистр. } в waitSpi() с оптимизацией не задерживаюсь ни когда, а должен не задерживаться только первый раз. После отправки данных до чтения регистра SPI1->SR проходит много времени, но меньше, чем выход 8 бит в спи.
|
|
|
|
|
Jun 21 2018, 11:48
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 21 2018, 13:50)  inline void waitSpi() { while( (SPI1->SR & (uint8_t)SPI_FLAG_BSY));} - вот этот код не работает с оптимизацией в пустом хороводе. И не изредка - а всегда. 100 раз из 100. А без оптимизации работает как часы. ни каких сбоев в обмене по SPI замечено не было. Описание типов данных, какое ядро/МК, описание SPI_FLAG_BSY и декларации SPI1 где? Асм-результат с оптимизацией где? Или с декларациями что-то не так или у вас в этом коде например гонки или куча других причин. И опять - пример никак не свидетельствует о вине компилятора. Вот приведу такой случай из своей практики: Писал я как-то под CC5502 (TI DSP). Был простейший цикл типа Вашего выше: просто в цикле опрашивался флаг, типа: while (!flag); Этот flag в состояние !=0 устанавливался в ISR завершения DMA-транзакции (цикл ждал завершения DMA). И вот у меня возникла ситуация подобная Вашей: без оптимизации или с минимальной оптимизацией всё работает, а с полной оптимизацией - виснет в бесконечном ожидании в этом цикле и через некоторое время вылетает в bus-fault в DMA-контроллере. А дальше стало ещё хуже - выяснилось, что при добавлении/убирании строчек кода из совершенно других мест си-исходника, этот код иногда начинает работать. Причём работать 100%! Но при следующем редактировании исходника опять начинает падать в bus-fault (внимание adnega - очень знакомое поведение, не правда-ли?  ) Видимо Вы бы уже сложили ручки и обвинили во всём компилятор. Но я продолжил рыть. И в конце-концов нашёл причину! Оказалось, что при полной оптимизации код цикла получался такой короткий и быстрый, что CPU всё время был занят подкачкой команд в буфер предвыборки, только загрузил в конвеер команду условного перехода, пока грузил следующий пакет предвыборки за ней, то команда успевала выполниться, шёл сброс конвеера и загрузки предвыборки из начала цикла. Вобщем - тело цикла было длиной всего 2-3 такта. И если получалось что тело цикла располагалось на границе сегментов предвыборки (смещение начала цикла в памяти & 3 было == определённым значениям), то процессор полностью загружал шину к памяти своими запросами. А у него приоритет доступа к шине был выше чем у DMA-контроллера. DMA-контроллер считав данные из периферии, делал 512 попыток запроса доступа к шине ОЗУ у арбитра, не получал доступа и в конце концов останавливал работу с выставлением fault-а. При добавлении/удалении строк кода в разных местах исходника, весь код сдвигался, соответственно - чтение тела цикла иногда влезало в 1 сегмент prefetch (требовало на 1 такт меньше времени) и код чудесным образом начинал работать (DMA-контроллер в этот 1 освободившийся такт успевал пролезть на шину и записать данные в ОЗУ). Вот такой пример из практики. И что же - тут виноват компилятор? Нет, виноват конечно был я - не надо было располагать DMA-буфер и код в одном сегменте ОЗУ (МК не позволял задать для DMA приоритет доступа к шине выше чем CPU)
|
|
|
|
|
Jun 21 2018, 12:01
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(jcxz @ Jun 21 2018, 14:12)  Из Вашего описания я понял, что асм-файл писали Вы сами.... Как телепат телепату, откройте секрет, где в моем сообщении можно хоть как-то прийти к такому выводу? В строчке make.EXE: *** [obj_sw/esp8266.o] Error 1 видим, что не собирается цель esp8266.o А ошибку видим в s-файле с роботским именем C:\Users\user\AppData\Local\Temp\ccShzzAY.s в месте похуже корзины. Т.е. вы решили, что мой проект лежит в Temp, я сам написал asm-файл и дал ему имя ccShzzAY.s, а затем как-то заставил компилятор ругаться при сборке esp8266.o причем русским языком ниже сказано про Си-исходник? КАК?
|
|
|
|
|
Jun 21 2018, 12:11
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 21 2018, 14:46)  в waitSpi() с оптимизацией не задерживаюсь ни когда, а должен не задерживаться только первый раз. После отправки данных до чтения регистра SPI1->SR проходит много времени, но меньше, чем выход 8 бит в спи. Как одна из возможных причин: Загружаете данные в буферный регистр SPI. Флаг DLY при этом не выставляется. А выставляется он только после того, как данные из буфера попадут в сдвиговый регистр. А попадут они туда после того как пройдёт некая задержка от завершения предыдущего SPI-фрейма (предыдущего поднятия CS=high), выдержки межфреймовой паузы (при CS=high), выдержки паузы после CS=fall и началом первого клока SCLK. Вот в этот момент возможно данные переместятся в сдвиговый регистр и выставится флаг DLY. Т.е. - от момента записи данных в буферный регистр SPI до момента выставления флага DLY может пройти существенное время (в зависимости от частоты SCLK). А у Вас в программе возможно вы после записи очередной порции данных в буферный регистр SPI сразу переходите к waitSpi() и считаете что данные уже передались (так как флага нет), а они по-настоящему ещё и не начинали передаваться. При запрете оптимизации же, код выполняется медленнее и DLY успевает выставиться, поэтому и работает. Вот такой тип багов и называется "гонки". Как я выше и писал. И он конечно ваш, а не оптимизатора. PS: Это как возможный вариант причины бага. Причина может быть и другой. Так как всего кода я не вижу и как работает SPI в вашем МК - не знаю. Цитата(adnega @ Jun 21 2018, 15:01)  А ошибку видим в s-файле с роботским именем C:\Users\user\AppData\Local\Temp\ccShzzAY.s в месте похуже корзины. Т.е. вы решили, что мой проект лежит в Temp, я сам написал asm-файл и дал ему имя ccShzzAY.s, а затем как-то заставил компилятор ругаться при сборке esp8266.o причем русским языком ниже сказано про Си-исходник? КАК? Я не смотрю на имена файлов тем более пути. Достаточно только расширения. И про си-исходник там сказано только что что-то в нём меняете. Как вариант: после смены там функция становится больше, перемещается в другое место в памяти и эта функция вызывается из асм между метками .L274, .L276 - и команда вызова становится длиннее и расстояние между .L274 и .L276 увеличивается. PS: Если хотите чтобы Вас понимали однозначно, излагайте свои мысли ясно.
|
|
|
|
|
Jun 21 2018, 12:29
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(jcxz @ Jun 21 2018, 16:48)  описание SPI_FLAG_BSY и декларации SPI1 где? Вы мои посты читаете? все описания и декларации в приведённом коде. Цитата Как одна из возможных причин:.... Я же говорю, вы не читаете мои посты. Смотрите код. Наверно на си не понятно, напишу на русском. CS - нет. СПИ-мастер. 1)спим секунду. если что-то и было в спи, то давно вышло. 2)готовим данные.... 3)проверяем бизи - waitSpi() 4)записываем в SPI1->DR данные. Данные пишутся в тх регистр, и потом копируются в шифтРегистр. в этот момент должен встать флаг бизи. 6)если ещё есть данные для передачи, goto 2) с момента записи в SPI1->DR до waitSpi() проходит не 1 такт цпу. Тут один поток...проверка на кол-во переданых данных, подготовка данных не быстрая, спешить не куда, всё равно ждать waitSpi().
|
|
|
|
|
Jun 21 2018, 12:39
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(juvf @ Jun 21 2018, 15:29)  CS - нет. СПИ-мастер.
1)спим секунду. если что-то и было в спи, то давно вышло. 2)готовим данные.... ... Суть в том, что вы замели проблему под ковёр, не разбираясь в причинах. В условиях цейтнота такое бывает оправдано, а вообще это некрасиво, не говоря уже о том, что эта проблема может вылезти в будущем. Плюс опыт - он ведь сын ошибок трудных. Вот, собственно, и всё. Лезть глубже в вашу систему сейчас едва ли продуктивно.
|
|
|
|
|
Jun 21 2018, 12:57
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(jcxz @ Jun 21 2018, 15:11)  PS: Если хотите чтобы Вас понимали однозначно, излагайте свои мысли ясно. Дык, вы же заявляете: "Я не смотрю на имена файлов тем более пути". Может, вам стоит более вдумчиво читать, что тут пишут, вместо упражнений в фантазировании? (которое уже даже не смешно). Сейчас вам ясно, что виноват компилятор, а не я? Сказанное "В приведённом Вами примере баг в Вашем коде, а не в компиляторе" не желаете опровергнуть и извиниться?
|
|
|
|
|
Jun 21 2018, 13:09
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(jcxz @ Jun 21 2018, 17:11)  Вот такой тип багов и называется "гонки". Как я выше и писал. И он конечно ваш, а не оптимизатора.  Допустим, гипотетически.... записал в спи-сд данные, что-то сделал пусть будет проверка for(), проверил бизи. между записью и бизи было времени достаточно для выставлдения флага бизи. нужно 1 мкс, f было 10 мкс. Код рабочий, глюков нет. Может такой код перваться прерыванием и 10 мкс могут только увеличиться.... до 100, до 200.... код от этого не упадет и всё будет работать. Гипотетически! Код for() { waitSpi(); spi->sd = data_out; somethinkDo(); } Допустим такой код работает без оптимизации..... включили оптимизацию и появилась ваша гонка.... опять же гипотетически.... Вот вы взяли асм.... и начали исследовать, что там не так... и нашли ГОНКУ!!! Как её убрать? не нужно сразу читать SR, а нужно подождать начало передачи. Что делать? Поставить паузу.... Т.е. поставите оптимизатор, чтоб было быстрее, и потом поставите паузу, чтоб не было гонки? Я просто отключу оптимизатор в нужном месте и всё. В таком случает я считаю нет ошибки в неоптимизированном исходном коде и нет ошибки компилятора. Есть одно - при включении оптимизатора код перестает работать. Я вам не пытаюсь доказать, что есть ошибка компилятора. я не знаю. я говорю, что при включении оптимизатора на весь код, на код без ошибок и без глюков - код может лечь. И это не ошибка кода. Цитата Суть в том, что вы замели проблему под ковёр, не разбираясь в причинах. В условиях цейтнота такое бывает оправдано В чем то я с вами соглашусь.... Про цейтнот согласен, про проблему под ковром - нет. Дело в том, что проблема такая "оптимизированный waitSpi() не работает". Тут два пути: 1 - не использовать оптимизированный waitSpi(), 2 - искать причину в оптимизированном waitSpi(). Посмотрите код waitSpi - там по сути оптимизация не нужна. Там нужно указать volatile, что делается в SPL. Весь код функции - одна строчка. Я пошел по пути наименьшего сопротивления. Тоже самое можно сказать про црц и про другие случаи (не буду всё сюда валить). У меня ещё не было случаев, чтобы оптимизатор оголял ошибки в коде. Если есть ошибка в коде - она находится без всяких оптимизаторов. Горе программист, которому для нахождения ошибки нужен оптимизатор.
|
|
|
|
|
Jun 21 2018, 13:34
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(juvf @ Jun 21 2018, 16:09)  Т.е. поставите оптимизатор, чтоб было быстрее, и потом поставите паузу, Именно. Без for(delay = 0; delay < 20; delay++) __NOP(); не работает складно в железе, ибо таймер питается частотой с предделителя и запись в регистр MDR_TIMER3->CH1_CNTRL1 не приводит к должному результату, если после записи MDR_TIMER3->CH1_CNTRL не установить нужное число NOPов. Код MDR_TIMER3->CH1_CNTRL1 = CH_CNTRL1_FORCE_0; MDR_TIMER3->CH1_CNTRL = (0 << MDR_TIMER_CH_CNTRL_OCCM); MDR_TIMER3->CCR1 = last_cnt + temp; MDR_TIMER3->CH1_CNTRL = (3 << MDR_TIMER_CH_CNTRL_OCCM); for(delay = 0; delay < 20; delay++) __NOP(); MDR_TIMER3->CH1_CNTRL1 = CH_CNTRL1_WAIT_RISE; //for(delay = 0; delay < 20; delay++) __NOP(); MDR_TIMER3->STATUS = ~(1 << MDR_TIMER_STATUS_CCR_REF1_EVENT); //for(delay = 0; delay < 20; delay++) __NOP(); MDR_TIMER3->IE |= (1 << MDR_TIMER_IE_CCR_REF1_EVENT_IE); pulse[0].state = PULSE_STATE_WAIT_RISE; Телепаты в курсе, что это миландровский К1986ВЕ92 попил мою кровушку.
|
|
|
|
|
Jun 21 2018, 13:40
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(adnega @ Jun 21 2018, 16:34)  Телепаты в курсе, что это миландровский К1986ВЕ92 попил мою кровушку. А не лучше ли вот так: Код MDR_TIMER3->CH1_CNTRL = NEW_VALUE; while (MDR_TIMER3->CH1_CNTRL != NEW_VALUE); ???
|
|
|
|
|
Jun 21 2018, 13:56
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(scifi @ Jun 21 2018, 16:40)  А не лучше ли вот так: Код MDR_TIMER3->CH1_CNTRL = NEW_VALUE; while (MDR_TIMER3->CH1_CNTRL != NEW_VALUE); ??? Не пройдет. Я так понял, что там это не просто регистр, а какой-то командный центр. Запишется-прочитается на HCLK, но реальное железо отработает, только когда на него клок придет с предделителя. Вроде, с RTC такое бывает - там нужно ждать синхронизации с блоком 32кГц, но там и битик есть, что синхронизация состоялась.
|
|
|
|
|
Jun 21 2018, 14:21
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 21 2018, 15:29)  Я же говорю, вы не читаете мои посты. Смотрите код. Наверно на си не понятно, напишу на русском. Да пишите хоть на китайском, мне ваш код безразличен, это ведь не у меня при включении/выключении оптимизации он то работает, то нет. Мои исходники работают вне зависимости от того включена оптимизация или нет, у меня таких проблем нет. Цитата(juvf @ Jun 21 2018, 15:29)  1)спим секунду. если что-то и было в спи, то давно вышло. 2)готовим данные.... 3)проверяем бизи - waitSpi() 4)записываем в SPI1->DR данные. Данные пишутся в тх регистр, и потом копируются в шифтРегистр. в этот момент должен встать флаг бизи. 6)если ещё есть данные для передачи, goto 2) Должен да не обязан. Где между п.4 и п.6 проверка, что бизи установился? "Вы мои посты читаете"? Я о таком кривом подходе как здесь ещё несколько постов назад написал. Вот как раз такой код и будет работать в зависимости от уровня оптимизации, количества передаваемых данных, фаз луны.... А, ну да, зачем читать и думать, ведь всё уже выяснено - компилятор виноват!  Цитата(adnega @ Jun 21 2018, 15:57)  Сказанное "В приведённом Вами примере баг в Вашем коде, а не в компиляторе" не желаете опровергнуть и извиниться? Нет, не желаю так как причина баг не найдена. Вы не доказали никак вину компилятора. Да собственно там у вас и не баг в выполнении программы, а ошибка компиляции. Цитата(juvf @ Jun 21 2018, 16:09)  Допустим такой код работает без оптимизации..... включили оптимизацию и появилась ваша гонка.... Гонки у Вас всегда есть, а не только при вкл. оптимизации. Почитайте в инете что это такое. Надо писать код, свободный от такого. Цитата(juvf @ Jun 21 2018, 16:09)  Как её убрать? не нужно сразу читать SR, а нужно подождать начало передачи. Что делать? Поставить паузу.... Т.е. поставите оптимизатор, чтоб было быстрее, и потом поставите паузу, чтоб не было гонки? Я просто отключу оптимизатор в нужном месте и всё. Правильно тут уже написали про Ваш стиль: Вместо подумать и понять как сделать правильно, замести проблему под ковёр. В том цикле достаточно всего лишь после записи данных в SPI, дождаться начала их передачи (выставления бизи). Но конечно это кривой метод, так как потребует запрета прерываний (чтобы вся передача не проскочила за время внезапно возникшего прерывания). А чтобы написать правильно и корректно нужно вообще весь алгоритм этот изменить, использовать другие флаги, прерывания и пр. Цитата(juvf @ Jun 21 2018, 16:09)  Горе программист, которому для нахождения ошибки нужен оптимизатор. Ещё горше тот, которому оптимизатор её нашёл, указал, а он прячет голову в песок. Цитата(adnega @ Jun 21 2018, 16:34)  Телепаты в курсе, что это миландровский К1986ВЕ92 попил мою кровушку. И DSB/DMB не спасают? Цитата(adnega @ Jun 21 2018, 16:56)  Запишется-прочитается на HCLK, но реальное железо отработает, только когда на него клок придет с предделителя. Тогда должны быть какие-то статусные биты, говорящие о завершении операции периферией.
|
|
|
|
|
Jun 21 2018, 14:44
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата Правильно тут уже написали про Ваш стиль: Вместо подумать и понять как сделать правильно, замести проблему под ковёр. Нет, не правильно. Есть задача - написать текст. Есть 2 карандаша. Один пишет, другой не пишет. Я не буду тратить время на выяснение, почему не пишет один карандаш, если я могу обойтись без него. Я другим карандашом текст напишу. Вы выяснили и устранили, почему при вкл холодильника виснет CCS? Вы замелм пролему под ковёр заменили кабель усб и продолжили работать. Цитата Ещё горше тот, которому оптимизатор её нашёл У меня оптимизатор ошибок не нашел. Цитата мне ваш код безразличен Зачем вы просили код в студию? Хотели найти в нем ошибку. Нашли? Или чтоб написать "мне ваш код безразличен"? Цитата при включении/выключении оптимизации он то работает, то нет Где я писал, что "то работает то нет"? У меня такого кода нет. Цитата А, ну да, зачем читать и думать, ведь всё уже выяснено - компилятор виноват! Что компилятор виноват - это ваши слова, не нужно их мне приписывать. Я же говорю вы не читаете мои посты, не вижу смысла дальнейшей дискуссии с вами по этому поводу.
|
|
|
|
|
Jun 21 2018, 14:47
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(jcxz @ Jun 21 2018, 17:21)  Нет, не желаю так как причина баг не найдена. Вы не доказали никак вину компилятора. Да собственно там у вас и не баг в выполнении программы, а ошибка компиляции. Если компилятор не может собрать корректный код, то по вашему это не баг компилятора? Вы понимаете, что компилятор, компилируя esp8266.c, создал временный s-файл, в котором смещение им же считается неверно? А при эквивалентной перестановке строк в Си-исходнике он может собирать или не собирать исходник. В этом я виноват? Цитата(jcxz @ Jun 21 2018, 17:21)  И DSB/DMB не спасают? Очевидно же нет, т.к. у таймера как у периферийного блока своя собственная частота. Пример из errata на этот проц привел в скрине. "Обеспечивать необходимую задержку" тупо NOP`ами. Насчет MDR_TIMER3->CH1_CNTRL там же есть бага 0013: "В регистре есть бит, но он всегда читается как 1, при этом запись в него корректно задает режим". Цитата(jcxz @ Jun 21 2018, 17:21)  Тогда должны быть какие-то статусные биты, говорящие о завершении операции периферией. А если их нет? В данном случае их нет, и производитель в аналогичной ситуации рекомендует "обеспечивать необходимую задержку".
Эскизы прикрепленных изображений
|
|
|
|
|
Jun 22 2018, 05:11
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(jcxz @ Jun 21 2018, 12:05)  Цитата(juvf @ Jun 21 2018, 09:24)  Посмотрите сколько лишних операций в for у ТС! какие-то приведения типов, операторы, << и |, дополнительный j, операции j++!!! УЖАС!!!
И что? Современные оптимизаторы творят чудеса. И им по-барабану Ваши жалкие потуги заменить операции индексирования на указатели и подобное - они это и сами хорошо делают, даже ещё и лучше. Так что если бы код автора не был такой кривой, результат работы оптимизирующего компилятора мог быть лучше memcpy(). Цитата Цитата Попробуйте когда-нить попробую обязательно. Добрый день фантазёры, эльфы, а также программисты. Совсем недавно (2017-2018 год) занимался оптимизацией кода в STM8. Ручные потуги оптимизации давали очень хороший результат. Оптимизатор конечно оптимизировал код, но некоторый несложный рефакторинг давал значительно лучший результат. Может стм8 сырой? может арм действительно рулит? Проверил на арме.... собрал код ТС, улучшенный код ТС, а также просто memcpy(). В трёх случаях заполнял массив uin16_t [16] из массива uin8_t[32]. Измерял осциллографом. улучшенный код ТС выглядит так: Код #define SIZ 32 uint8_t dataOut[SIZ+2]; uint16_t buffer[SIZ/2]; uint8_t *p1 = (uint8_t*)buffer; uint8_t *p2 = (uint8_t*)&dataOut[0]; ... GPIO_SetBits(GPIOA, GPIO_Pin_5); //ТР2 = 1 //оптимизированный варинат ТС for(int i = 0; i < SIZ; i++) p1[i] = p2[i]; GPIO_ResetBits(GPIOA, GPIO_Pin_5); Результат - (код TC с "<<", "|", "++", "j") / (улучшенный код ТС) / ( говноmemcpy() ) Не оптимизированный код дал результат 8/5/1.3 мкс с оптимизацией по скорости -Ohs результат 3/0.5/0.5 мкс кагбэ в примере всё выровнено к 4. Но серийные данные не всегда выровнены, поэтому усложнил задачу МК и приблизил к реальности, uint8_t *p2 = (uint8_t*)&dataOut[1]; получил без оптимизациии 8/5/2мкс, с оптимизацией -Ohs 3/2/1.7 мск Результат на лицо. Априори. Вот вам и наряд в не очереди современные оптимизаторы!!! ps проверял на стм32ф103, компилятор иар.
|
|
|
|
|
Jun 22 2018, 06:55
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(juvf @ Jun 22 2018, 08:11)  Добрый день фантазёры, эльфы, а также программисты. Совсем недавно (2017-2018 год) занимался оптимизацией кода в STM8. Ручные потуги оптимизации давали очень хороший результат. Оптимизатор конечно оптимизировал код, но некоторый несложный рефакторинг давал значительно лучший результат. Может стм8 сырой? Кстати, стм8 - особый случай. Там нет выдающихся компиляторов. Яр на уровне исходника умеет выкидывать лишние переменные и т.д., но машинный код делает плохенький. Видимо, избрана плохая стратегия генерации кода, и/или реализация подкачала. Они там придумали виртуальные регистры, предположительно, чтобы компилятору было удобнее. Результат так себе. Недавно попробовал космик, там код генерится практически в лоб, строчка исходника - несколько инструкций. Каждая локальная переменная выделяется на стеке. Результат в целом лучше, чем у яра. Ну и зная о прямом соответствии исходника и машинного кода, простор для микрооптимизаций на уровне исходника огромный.
|
|
|
|
|
Jun 22 2018, 07:37
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(juvf @ Jun 22 2018, 08:11)  ps проверял на стм32ф103, компилятор иар. 1.Не держите всех тут за дураков: эта ваша "проверка" - это для домохозяек аргумент. В первом случае что именно проверили? Что в случае цикла и в случае memcpy() компилятор очевидно заменил и то и другое циклом типа: Код MOVS Rn, #32/4 p: LDR Rx, [Ry], #4 STR Rx, [Rz], #4 SUBS Rn, Rn, #1 BNE p или вообще развернул цикл в линейный код? Так я об этом ещё много постов назад говорил, что Ваш memcpy() в бОльшей части случаев не даст выигрыша. И далее - нашли какой-то случай, когда определённый компилятор, при определённых ключах компиляции даст небольшой выигрыш? Так я опять же уже давно говорил, что при определённых условиях и memcpy может быть быстрее. А теперь попробуйте задать размер переменным и указатели переменными, так чтобы они не могли быть вычислены на этапе компиляции. Измерить всю функцию а не какой-то непонятно как выбранный участок. Вывод из этих "проверок" можно сделать только один: компилятор оказался умнее вас. 2.Измерять миллиметровые расстояния линейкой с метровыми делениями - это типичный ардуино-подход. Позвольте узнать - сколько времени заняли ваши конструкции с установками/сбросами GPIO? Как они оптимизировались в случаях разных тестов? У вас они дали больше погрешности чем весь "измеряемый" код. Нормальное измерение длительностей (тем более таких малых) делают по таймеру, но никак не по GPIO. Вся ваша проверка яйца выеденного не стоит. Этими проверками Вы как раз и показали всем тут свой "уровень" программирования и понимания сего процесса. И проверять там нечего, достаточно взглянуть на листинг как Вам тут уже неоднократно говорили. Впрочем похоже для вас это пустой звук. Так же как и о самом предмете оптимального программирования Вы не имеете никакого понятия.
|
|
|
|
|
Jun 22 2018, 09:13
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
ваш ответ - тоже априори. много слюней, воды, телепатии и всё мимо. Так ведут себя диванные эксперты. Цитата(jcxz @ Jun 22 2018, 12:37)  компилятор очевидно заменил и то и другое циклом типа: Код MOVS Rn, #32/4 p: LDR Rx, [Ry], #4 STR Rx, [Rz], #4 SUBS Rn, Rn, #1 BNE p даже близко не угадали. ни одной строчки кода )))) Цитата или вообще развернул цикл в линейный код? - опять мимо Цитата что Ваш memcpy() в бОльшей части случаев не даст выигрыша. ещё раз мимо. Цитата Позвольте узнать - сколько времени заняли ваши конструкции с установками/сбросами GPIO? Сначала проверил, сколько времени будет установка и сброс GPIO - с -Ohs составило 300 нс в каждом тесте. Результат показанный ранее, был с учетом погрешности GPIO. Цитата Нормальное измерение длительностей (тем более таких малых) делают по таймеру, но никак не по GPIO. Нормальные измерения длительностей делают осциллографом, анализаторам, а не внутренними таймерами. Осциллограф даст более точное измерение (тем более он поверенный), нежели внутренний таймер МК, когда частота МК на порядок меньше полосы осциллографа. И генератор МК ±вагонИтележка, по сравнению с поверенными средствами измерения. Что ваш внутренний таймер, когда система должна среагировать на внешние прерывание/событие, обработать данные и выдать признак готовности, например дернуть регистр GPIO/DMA/UART? Это реальное время и реальный мир, а не ваша Нарния ваши фантазии с таймерами. Как раз таки осциллограф даст точность в мкм, а таймер в метрах. Вы предлагаете Измерять миллиметровые расстояния линейкой с метровыми делениями - это типичный эльфо-подход. Цитата И проверять там нечего, достаточно взглянуть на листинг Дак посмотрите, оторвитесь от дивана и посмотрите, вместо того чтобы тут сочинения писать. Я то посмотрел.... исходный код ТС с << | j более грамоздкий в асме, нежели чем без этих лишних операторов или memcpy(). Впрочем похоже для вас это пустой звук. Так же как и о самом предмете оптимального программирования Вы не имеете никакого понятия.Оставайтесь прибывать в своих фантазиях.
|
|
|
|
|
Jun 22 2018, 18:10
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(jcxz @ Jun 22 2018, 14:31)  Ну, даешь, ядрена вошь! И олень тебе не гож? А вчерась мытарил душу: Вынь оленя да положь!.. То дай код, в нём ошибка! то новый вымыслы про стек... то внезапно код мой безразличен... то предложение проверить да проверь... то проверять там нечего... виртуоз-переобувальщик. а когда все таки проверил - вдруг проверка неугодная стала!!! аргументы кончились, понеслось что-то абстрактное, типа "кучи разных факторов" и оскорбление негативная оценка оппонента, типичное поведения.... ну вы поняли кого... Цитата Попробуйте когда-нить не теоретизировать внустую, а скомпилировать и посмотреть разные варианты с копированием в цикле на разных компиляторах с включённой полной оптимизацией по скорости и с разными условиями цикла (короткий цикл/длинный, условия окончания и указатели - переменные или известны на этапе компиляции). И будете удивлены. вот тут вы правы, соглашусь!!! Очень удивился. memcpy() на порядок быстрее for-loop. Перепробывал с разными вариантами, с переменными размерами массива и указателями, неизвестными на этапе компиляции. в тактах процессора примерно 90 к 700(без gpio, таймеров и прочей "кучи разных факторов"). Причем оптимизация практически не влияет на memcpy(). Он всегда быстрый. Вашим for даже и не снилась такая скорость. устали вы меня.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|