Jump to content

    

sigmaN

Свой
  • Content Count

    2605
  • Joined

  • Last visited

Everything posted by sigmaN


  1. Разобрался. Отвечу сам себе. Если станция принимает фрейм со своим адресом и установленным P/F битом то это команда и P/F бит трактуется как P бит. Если станция принимает фрейм с адресом собеседника и установленным P/F битом то это ответ и P/F бит трактуется как F бит.
  2. В общем, для разнообразия решил реализовать HDLC в ABM режиме. Вооружился первоисточником ISO/IEC 13239:2002(E) С фуркцией P/F бита в других режимах всё ясно. Читаю про ABM... Говорят можно в любое время слать фреймы с F=0. Зачем и в каких ситуациях слать фрейм с P=1 я из этого текста не понял... Ну, мол, придёт вам P=1 ответьте c F=1 побыстрее.... А когда он придёт, когда отправлять... Не понятно. Честно говоря я во всем тексте не нашел этого пока. Тут вроде ситуация проясняется... Если хотим начать чекпоинтинг то засылаем фрейм с P=1, а по ответному фрейму с F=1 понимаем, какие фреймы подтверждаются получателем. Но всё это пока не проясняет вопрос когда же мне отправлять фрейм с P=1? Ответ меня ждал в конце документа. Обе станции начинают обмен с отправки фрейма с P=1. Далее ставят этот бит снова, как только получат ответ с F битом.... Т.е. ставят его по принципу "как можно скорее". Есть одно но: P/F бит это физически один и тот-же бит во фрейме...Он просто имеет семантическое значение P/F в зависимости от контекста(очередности появления в канале, на сколько я понимаю). Как станции различают P это был или F если они обе начинают обмен с отправки фрейма с установленным битом? Вот этого маленького, но важного момента я пока не понял... Кстати на стэковерфлоу тоже не поняли что это было и выдвинули версию, что это дескаать для консистентности с остальными режимами... Но не думаю, что всё так просто. Как минимум ради чекпоинтинга всё-таки стоит почаще отправлять фреймы с P=1 и ловить F=1.
  3. Можете подробнее описать вашу мысль? Как проектируют? Вот вы параметры CRC(не только длину, разумеется) как выбираете? А может быть нужно не только детектировать ошибки, но и восстанавливать(избыточное кодирование). А какие ошибки восстанавливать нужно? Сколько бит избытка будет нормально по вашему? Ну и ещё там по мелочи что-нибудь расскажите немного об этом, если можно. Очень интересно как оно в реальности делается....
  4. Ну я ваш стиль нагнетания ситуации и наведения ужаса уже понял, да. Спасибо )
  5. Начали за здравие, как говорится..... Изначально был вопрос о реализации протоколов, которые уже разработаны и все мат.модели выверены.
  6. Т.е. вы просто потом предлагаете автоматически генерировать С/С++ и радоваться жизни? Спасибо, интересно.
  7. Ноги видимо растут из убежденности в незаменимости голого Си. Дескать все эти классы и абстракции только код раздувают да память жрут.. Ну давайте на чистоту: мне тоже не нравится эта библиотека. Просто потому, что если её притащить в проект, то писать придется уже не на плюсах, а на том домэйн-специфик языке, который эта библиотека предлагает. Но чтиво было интересным. Надо бы аргументировать. Создаётся впечатление, что у вас когнитивное искажение в сторону Си) И компилятор вам не друг, а враг. Я провел достаточно времени за изучением именно этого проекта. Выстрелить в ногу там себе можно 18 разными способами в каждом модуле ) Именно из-за отсутствия возможности сделать нормальные абстракции на Си - авторами пришлось повсеместно использовать препроцессор, мэйджик намберы и т.д. На Си повсеместно приходится изобретать то this pointer то vtable то препроцессором костыли вставлять. Я тоже тогда позволю себе дать вам рекомендацию по исправлению когнитивного искажения.. Когда-ниубдь на досуге попробуйте просто взять тот-же pppd и переписать на плюсах. В качестве первого упражнения предлагаю следующее: Если где-то видите, что в группу функций передаётся некое состояние в виде указателя на одну и ту-же структуру - делайте это классом. Структуру эту в класс, функции делайте членами каласса. Спустя какое-то время вы сможете оценить тот факт, что благодаря this поинтеру это то-же самое(можете в ассемблер заглянуть) только лучше;) Лучше потому, что вы устанавливаете и контролируете инвариант в одном месте. Ограничиваете область видимости до нужной. И так далее... Разделяй и властвуй ;) Так, потихонечку можно будет и до свежих стандартов дойти и лично убедиться, что если на платформу есть компилятор с плюсов, то писать на голом си просто даже нет смысла. Нет причин. Кроме, конечно, самоотверженной веры в универсальность и незаменимость Си. P.S. немного не про эмбеддед конечно, но всё-же https://youtu.be/wbZdZKpUVeg Как вам такое? Тут тоже ни на миллиметр люди не приблизились к заветной мечте? )))
  8. Вот я давича смотрел исходники pppd https://github.com/paulusmack/ppp Что хорошего, светлого и умного можно почерпнуть из них? Такое я и сам накодить могу. ИМХО это стиль девяностых(восьмидесятых??) А чтобы вы лучше поняли мою позицию, я приведу тут чтиво, которое вчера нагуглил и перед сном успел полистать. http://caxapa.ru/thumbs/726398/comms-protocols-cpp.pdf Интересно, познавательно. Возникает желание попробовать, посмотреть, что там за бинарник получается...
  9. Всем привет! Посоветуйте какое-нибудь годное чтиво по реализации разных протоколов(HDLC, PPP, TCP/IP и т.д.) на С/С++. Какие-нибудь лучшие практики, шаблоны... Конечные автоматы как люди делают в таких случаях(eстественно, с упором на встраиваемые системы. Так то про Boost.msm мы слыхали). Правильная организация буферизации интересует в таких случаях, в условиях многопоточности особенно. Я обычно двойную буферизацию: писатель в пишет в один буфер, читатель читает из другого, ну и в критической секции только переключение этих буферов. В целом какое-то у меня ощущение складывается, что каждый раз как в первый раз изобретаю что-то на эту тему, хочется уже умных людей почитать и окончательно понять как правильно.
  10. Помогите скачать https://wenku.baidu.com/view/2f3378c30c22590102029d31.html Я даже нашел логин и пароль тут http://bugmenot.com/view/wenku.baidu.com Но при входе он просит ввести код из смс на номер телефона владельца аккаунта.
  11. Добрый день! Удалось ли найти что-нибудь? Поделитесь? Про фтп слышал, но там пока найдена только одна часть пазла.
  12. https://youtu.be/FJJTYQYB1JQ Годный доклад Александреску 2019 года по поводу сортировки. В том числе рассматриваются реализации std::sort в разных библиотеках. Add: ему удалось превзойти std::sort! Довольно неожиданным способом. В общем, Александреску в очередной рвз не подвёл ))
  13. Не так драматически. Не знаю как это мы видим? )) Лично у меня классическая рекурсивная реализация проигрывает как по скорости, так и по требуемой памяти. И вообще считаю, что если есть требования к скорости и есть возможность что-то реализовать не рекурсивно - нужно реализовывать не рекурсивную версию. Но да ладно, перейдем к делу. Вчера вернулся я к этой задаче. Думал, гадал... туда-сюда код вертел... Ускорения особого добиться не удалось. Сдаюсь, думаю. Пойду смотреть как stable_sort реализован в STL. Входная последовательность разбивается на группы по 32 элемента Каждая такая группа сортируется сортировкой вставкой(insertion sort) После этого начинаются объединения(merge) Так что стало ясно, что алгоритм просто другой. Я "по честному" сливаю всё начиная от групп по 1 элемента, а там всё начинается с групп по 32. Еще потом посмотрю, может мелкомягкие еще какие-то хитрости используют ))
  14. Алгоритм merge sort не корректно сравнивать с quick sort потому как merge - это стабильная сортировка(сортировка, которая не меняет относительный порядок сортируемых элементов, имеющих одинаковые ключи). quick таким свойством не обладает. Ответ на вопрос зачем можно встретить уже в названии топика. Ну и далее я на него неоднократно отвечал.
  15. Сравнение сделаю. Скорее всего там реализована не merge sort, а quick sort. За merge sort вроде как нужно идти к std::stable_sort но и то без гарантий, как мы понимаем. https://stackoverflow.com/questions/5038895/does-stdsort-implement-quicksort Студию обновил до 2019. Вообще лично мне больше хотелось не просто поставить рекорды скорости, а именно разобраться что к чему с кешами и так далее, в плане оптимизации быстродействия. Вот, добился ускорения, измерил. Можно воспринимать эти цифры как относительные измерения, не абсолютные ) Add: std::stable_sort(arr, arr + N); Array = 100000000 elements, sizeof() = 400000000 Timer = 3911853us (3.911853sec) std::sort(arr, arr + N); Array = 100000000 elements, sizeof() = 400000000 Timer = 2681706us (2.681706sec) Add2: беглое чтение реализации stable_sort показало, что merge sort там используется. Правда через несколько условий которые я не проверял. Для маленьких массивов меньше 32 элемента используется insertion sort для остальных разные варианты merge sort в зависимости от доступности временного буфера. Почитаю там потом внимательнее что происходит у них. Пока интересно самому по-оптимизировать, а не копировать подсмотренные решения. Add3: сравнение компиляторов студии 2013 и 2019 различий в скорости моей реализации не показало.
  16. На сколько я помню, реализация библиотеки Си для AVR имеет после main() бесконечный цикл. Возврат в ResetHandler не производится, потому как из него и не происходит вызова. Вместо вызова делается просто jmp куда надо - т.е. на начало стартап кода, который подготавливает Си окружение и прыгает в main(). Убрать из main бесконечный цикл и посмотреть в отладчике что будет дальше не составляет никаких проблем. Более того - этим способом можно посмотреть даже на то, что происходит ДО main. Нужно только поставить брэйкпоинт на reset handler и шагать по инструкциям.
  17. Или я вас не понял или вы сделали неправильный вывод из расчетов. Я мыслю так: Внутренний цикл сорта в итоге копирует весь массив, посредством вызова merge() нужное кол-во раз. Давайте упростим и забудем про сравнения. Нам 381.4МБ через шину памяти надо протолкнуть 28 раз, умножаем на 2, потому как чтение+запись. Итого 28*381,4*2 = 20,8ГБ. Вы утверждаете, что DDR3-1333 может около 11ГБайт/с. Таким образом, если бы мы сидели на памяти, то потребовалось бы 20,8 / 11 = 1,89сек. О том, что сейчас мы сидим на процессоре, а не на пропускной способности памяти говорит и тот факт, что изначально потребовалось 8 секунд, а правка merge() касающаяся только кол-ва условных переходов в цикле, дала ускорение до 4.75сек. При этом ни кол-во обращений к памяти ни размеры прокачиваемых через неё данных не изменилось, поскольку алгоритм сортировки не менялся и изменен быть не может (иначе это уже будет реализация не merge sort, а чего-то другого). Считаю, что только после того, как процессор сможет всё сделать за 1,89сек мы упремся в память и как бы я не оптимизировал код - ускорения наблюдаться не будет. Т.к. пропускная способность памяти действительно закончится, а требование переслать туда-сюда нужное кол-во байт никуда не исчезнет (см. выше про то, что это всё-таки merge sort и он должен им оставаться). Правильно ли я рассуждаю?
  18. Были проведены измерения на массиве 100'000'000 элементов. Размер примерно 400 мегабайт. Для более консистентных результатов new был заменен на временный массив(static глобальная переменная, такая-же, как и сортируемый массив). Перед сортировкой массив заполняется отсортированной в обратном порядке последовательностью. Время выполнения измерялось через QueryPerformanceCounter() перед и после вызова функции сортровки. i7-2630QM DDR3-1333 dual channel, Visual Studio 2013 флаги компилятора /Ox /Ot /Oy Исходная реализация(код в первом посте, new заменен на статический временный массив). Array = 100000000 elements, sizeof() = 400000000 Timer = 8241447us (8.241448sec) Модифицированная реализация быстрее на 43%: Array = 100000000 elements, sizeof() = 400000000 Timer = 4759489us (4.759489sec) Модификации подверглась только функция merge(), её новая реализация под спойлером. Если кратко, то разные условные переходы были по максимуму вынесены из цикла. Из полученных результатов я сделал вывод, что узким местом была не память, а процессор. Хотя изначально казалось, что т.к. между доступами к памяти нет никаких особых вычислений то упрёмся мы в любом случае в память и несколько if() туда-сюда "погоды не сделают". Это оказалось не так. Продолжу исследовать эту тему. Пока что посмотрел эти два видео https://youtu.be/Nsf2_Au6KxU https://youtu.be/WDIkqP4JbkE Потом еще раз почитал PDF со слайдами Scott Meyers: Cpu Caches and Why You Care https://www.aristeia.com/TalkNotes/codedive-CPUCachesHandouts.pdf
  19. Сильное заявление... На сколько мне известно, они обычно применяются после того, как выбран наиболее быстрый(в классическом понимании этого слова) алгоритм. Почитаю,посмотрю. Но общее представление о работе подсистемы памяти у меня есть. Я даже несколько лекций Александреску смотрел в оригинале. Где про высоконагруженные системы и про то, что бесконечный цикл лучше цикла с условием, сравнивать лучше с нулем, не располагать bool в структурах, которые должны быть горячими в кэше и так далее... О том, что выделение памяти это дорого мы в курсе. Если бы функция сортировки вызывалась в программе достаточно часто, а максимально возможный размер массива был бы заранее известен то я бы выделил память один раз и вертел всё там. Пока что не ясно как применить всё вышесказанное к алгоритму сортировки слиянием, который я реализовал выше. Пока что мне кажется, что по памяти я всегда хожу линейно. Читаю всегда из одного массива - пишу в другой. Кэшу и блоку предсказаний процессора все карты в руки - пусть грузит в кеш память наперед, как только сообразит, что как минимум по чтению я по памяти всегда иду линейно от меньших адресов к старшим. Ну наверно можно было бы двигаться на одном проходе на повышение адресов, а на другом проходе на понижение. Из соображений, что недавно записанные данные по стершим адресам сейчас "горячие" и двигаясь обратно от них мы попадем в кеш. Или я как-то неправильно мыслю?
  20. Приветствую! Господа, оцените пожалуйста мою не рекурсивную реализацию сортировки слиянием. Покритикуйте, оцените )) Чисто спортивный интерес. Прочитал описание алгоритма, запилил с нуля ради разминки мозга. Основная "фишка" в том, что в отличие от рекурсивной реализации требуется меньше памяти как по объему так и по кол-ву выделений. Один раз происходит выделение временного массива, равного по размеру исходному, далее массивы переключаются по очереди и происходит слияние групп из одного в другой. Размер группы увеличивается в два раза за проход внешнего цикла. В общем то всё то-же самое, что и в рекурсивной реализации, только немного быстрее и экономнее по памяти. P.S. ясно, что рекурсивную реализацию в принципе тоже можно докрутить до однократного выделения памяти и работы уже из неё, но речь не о том. void merge(int* dst, int dstSize, int* left, int leftSize, int* right, int rightSize) { int leftIdx = 0; int rightIdx = 0; //printf("merge(dstsize=%i, leftsize=%i, rightSize=%i)\n", dstSize, leftSize, rightSize); for (int i = 0; (i < dstSize) && ((rightSize - rightIdx > 0) || (leftSize - leftIdx > 0)); i++) { //left array was exhausted, merge from right if (leftSize - leftIdx == 0) { dst[i] = right[rightIdx]; rightIdx++; } //right array was exhausted, merge from left else if (rightSize - rightIdx == 0) { dst[i] = left[leftIdx]; leftIdx++; } //both left and right still have elements to merge else { if (left[leftIdx] < right[rightIdx]) { dst[i] = left[leftIdx]; leftIdx++; } else { dst[i] = right[rightIdx]; rightIdx++; } } } } void mergeSortNonRecursive(int *a, int arrSize) { int *a2; try { a2 = new int[arrSize]; } catch (const std::bad_alloc& e) { printf("Bad alloc in mergeSortNonRecursive(): %s", e.what()); return; } int *arrays[2] = { a, a2 }; int arrayIdx = 0; int nextArrayIdx = (arrayIdx + 1) % 2; int groupLen = 2; int leftOffset; int leftSize; int rightOffset; int rightSize; int destOffset; int destSize; do{ leftOffset = 0; leftSize = 0; rightOffset = 0; rightSize = 0; destOffset = 0; destSize = 0; for (int i = 0; i < arrSize; i += groupLen) { destOffset = i; leftOffset = i; leftSize = arrSize - i; if (leftSize > groupLen / 2 ) leftSize = groupLen / 2; rightOffset = leftOffset + leftSize; rightSize = groupLen - leftSize; if (rightOffset > arrSize - 1) rightSize = 0; else if (rightOffset + rightSize > arrSize - 1) rightSize = arrSize - rightOffset; destSize = leftSize + rightSize; //printf("destOffset=%i, leftOffset=%i, rightOffset=%i\n", destOffset, leftOffset, rightOffset); merge(arrays[nextArrayIdx] + destOffset, destSize, arrays[arrayIdx] + leftOffset, leftSize, arrays[arrayIdx] + rightOffset, rightSize); } arrayIdx = nextArrayIdx; nextArrayIdx = (arrayIdx + 1) % 2; groupLen = groupLen * 2; } while (groupLen <= arrSize); //doing final merge if needed if (destSize != arrSize) { //printf("doing final merge\n"); rightSize = destSize; rightOffset = arrSize - rightSize; leftOffset = 0; leftSize = arrSize - rightSize; destOffset = 0; destSize = arrSize; merge(arrays[nextArrayIdx] + destOffset, destSize, arrays[arrayIdx] + leftOffset, leftSize, arrays[arrayIdx] + rightOffset, rightSize); arrayIdx = nextArrayIdx; nextArrayIdx = (arrayIdx + 1) % 2; } //copy result to a[] if it is not already there if (arrayIdx != 0) { for (int i = 0; i < arrSize; i++) a[i] = arrays[arrayIdx][i]; } delete[] a2; }
  21. Нашел калькулятор от TI http://webench.ti.com/wb5/LDC/?DCMP=ldc&amp;HQS=sva-psp-ssp-ldc-awire-20150819-lp-webenchcoil-wwe#/spirals Почему-то он рекомендует не заполнять катушку более чем на 70% Говорит, что Coil fill ratio должно быть больше 0.3 Почему так? Какой физический принцип, лежит в основе этой рекомендации? Просто это ограничение идет немного в разрез с желанием намотать по-больше витков.
  22. Я так понял, мне надо сделать по-больше витков, заказать плату, а потом просто измерить индуктивность, посчитать номиналы деталей по аппноте и всё будет ок.