Telegram Web Link
Еще немножко unsafe Rust, но в этот раз каноничного. Победил push в Qt

У Qt есть родной конвейер событий - event loop, который основан на Signals и Slots. Вместе Signals и Slots представляют собой некие, чтобы было понятней современными терминами, multicast-каналы, когда одно событие могут слушать несколько обработчиков.

Тоесть вы создаете Signal или берете готовый (например у элемента "кнопка" - clicked), создаете Slots, которые по факту представляют собой closure или fn и подписываете их на сигнал.

Есть нюанс. Ritual имеет биндинги для сигналов, которые создают переменные вида QBox<SignalOfN>, но QBox в Ritual не имеет Send. Тем не менее, сигналы в Qt - полностью thread-safe, это рекомендуемый и официальный способ передачи данных между потоками.

В таком случае, нам помогут некоторые крейты, например force-send-sync или unsafe-send-sync. Эти крейты сделают unsafe-обертки вокруг чего угодно, пользуйтесь на свой страх и риск и можете прилепить Send/Sync хоть к Rc.

В нашем случае, заворачиваете нужный сигнал, например в force_send_sync::Send и спокойно его пересылаете. При вызове emit-метода сигнала, нужный Slot выполнится не в текущем треде, а в основном.

Qt дает сигналы, которые могут передавать примитивы а также его родные объекты. Но что делать, если нужно передать что-то сложное?

В таком случае создайте простой Qt-cигнал, например SignalOfBool (SignalOfVoid не бывает), в дополнение к каналу. Затем из соседнего треда данные передаются как

tx.send(data).unwrap();
signal.emit(true);


А Slot, в свою очередь, просыпается и разбирает receiver:

while let Ok(data) = rx.try_recv() {
// process the data
}
👍5👎1
В новых HP Pavilion кнопка power расположена очень оригинально.

Предлагаю не останавливаться и передвинуть её в следующий раз куда-то ещё. Между G и H - в самый раз.
💩31😁15👍6🔥5
Коллеги, а посоветуйте для человека "за 30" первую книжку по IT. Очень один старый друг просит.

Энциклопедию професора Фортрана в 2022 советовать наверное не следует..
👍6😁2
Кстати о терминах. Тут недавно на фабрике одной разговор был:

- вы делали компании такой-то BMS?
- да, для их ветряных электростанций
- а там большое здание?
- откуда мы знаем? нам какая разница
- но вы же ставили им BMS - Building management system!
- да, мы ставили им BMS. Battery management system
👍14😁6
Релизимся. Написали с 50 тестов (на pytest). Нашли одну багу, и то случайно, в основном баги в самих тестах.

Тестируем Python-тесты софтом на Rust. И такое бывает.
😁16
Кстати о баге. Оказывается, если в mqtt отправить subscribe / unsubscribe bulk на 0 топиков, большинство имплементаций сервера тебя просто немедленно выкинут из сессии.

Без объяснений.
👍8
Поскольку бекгоф в качестве альтернативы предлагает TwinCAT/BSD, а TwinCAT/BSD - это FreeBSD с парой патчей, плюс TwinCAT-стек, а наши клиенты любят бекгоф, придётся наверное поддерживать и FreeBSD. Вот уж не думал, последний раз её видел, когда 4 уже была stable, а 5 ещё current.

Как на FreeBSD с Rust? А на удивление очень неплохо. Есть некоторые нюансы линковки сишных библиотек, в остальном, если что-то не сильно Linux-завязанное, хватает cargo build --release.

Как на FreeBSD со скоростью? На удивление, опять же очень неплохо. Наши приложения с tokio даже умудряются выдать на 2-3% больше попугаев, чем на Linux.

Как ощущения? Как будто вернулся к бывшей, с которой не виделись 20 лет. Посмотрим, что из этого получится.
👍24
Об async sleep в Rust. tokio::time::sleep на мелких интервалах использовать нельзя - у него минимальная остановка >1ms

Я раньше использовал крейт tokio_timerfd:sleep, который использует линуксовый timerfd на CLOCK_REALTIME, но в связи с тем, что нам приходится добавлять в продукты больше платформ, перешел на async_io::Timer::after, который работает в т.ч. и под винду и под *BSD.

Результаты на 100 тысяч sleep'ов, без realtime-ядра и выделения процессоров, но с фиксацией топовой частоты головы, следующие (Linux/BSD примерно одинаково):

async_io: avg sleep 106us / expected: 100us
tokio_timerfd: avg sleep 108us / expected: 100us

Так что, если кому нужен sleep < 1ms, рекомендую просто брать async_io - будет и кроссплатформенно, и немного более аккуратно.
👍10💩2
Немного обозрю TwinCAT/BSD от Beckhoff. Как уже говорил, это FreeBSD но немного патченная. В сам TwinCAT лезть не буду, только обзор самой системы. Для справки, кто не в курсе, TwinCAT - это такой промышленный Ethernet (на самом деле TCP/IP), с замашками на real-time.

Версии TwinCAT/BSD и FreeBSD, к счастью совпадают. Сейчас можно спокойно скачать и потестировать TC/BSD 13, который собран на FreeBSD 13.0 (кастомные бинарники от 13.1 не подойдут - там ABI уже FBSD_1.7, собирайте от 13.0).

Скачиваете zip-архив, в котором находится TCBSD-x64-13.iso, но на самом деле это никакое не ISO, а просто дамп диска с тремя разделами, включая EFI-loader. Подключаете с флешки или как HDD. На qemu у меня почему-то не поставился, что-то паниковал про APIC, лень было разбираться, поставил на VMware. Кроме х64 архитектур нет.

Инсталлятор - отболгеносен, лого фри заменено на BECKHOFF, на этом всё. Все опции выкинуты, можно выбрать только диск, куда ставить и пароль юзера (юзера создает с логином "Administrator"). После инсталляции флешку не вынимайте! У меня оно подключило оттуда своп. Потом бутнетесь, удалите своп, можете вынять.

Внутри - в целом обычная фря, но немного побитая. Зачем-то удалили ldd, можно закопировать из фри обычной. Весь TwinCAT управляется с помощью /usr/local/bin/TcSysExe.exe, который на самом деле не .exe а shell-скрипт. Из коробки есть две репы пакетов - своя и FreeBSD.

Вердикт: за потуги уйти с винды ставлю большой лайк. За сам продукт ставлю пока смайлик с какашкой. Реалтаймовость сети и прочего не тестировал - нужен реальный ящик. Доберусь попозже.
👍9😁3💩1
Дошла до меня мода, решил приобрести механическую клавиатуру. А то грамофон есть, борода есть, уже даже кофе пить начал, а клавиатуры нету.

Требуются кнопки Cherry MX blue и отсутствие подстветки, так как думаю под Linux она всё равно скорее всего работать не будет. Есть любители клавиатур? Накидайте любимых моделек в комменты. Желательно с нампадом.
👍4
Внезапно, если вы в нашем году вобьете в гугл или зададите вопрос на профильном форуме, какой самый лучший язык для программирования роботов, вам ответят в виде "C/C++, Python, Lisp, Matlab" и прочее. Староверы ответят ST/FBD.

Всё что угодно, кроме robot markup languages, которых еще недавно были десятки, а теперь они все хором умерли.
👍8
kids.push(std::thread::spawn(kid));
assert_eq!(kids.len(), 2);
👍21🔥4💩3
Перевел submap (https://crates.io/crates/submap) на BTreeMap/BTreeSet.

Немного рисерча.

Pub/sub-модели обычно подразумевают, что у клиента есть четкое ID, например в современных IPC-брокерах это его имя-строка, в более древних - numeric ID. Когда мы ищем, какие клиенты подписаны на топик, один клиент может попасть в список несколько раз - быть подписанным конкретно на топик и на него же с помощью масок и на выходе необходима дедупликация массива.

Если подписок и клиентов немного - спасает Vec::sort + Vec::dedup. На малых объемах (<20 объектов), эта связка обгоняет и HashSet и BTreeSet в любых вариантах (иногда даже в 2 раза), но далее скорость начинает предсказуемо падать, поэтому всё же лучше сразу применять сеты.

Сам по себе BTreeSet на такой задаче дает примерно +80% скорости, относительно std::collections::HashSet, я также пробовал разные альтернативные HashSet'ы и в принципе особой разницы не увидел. BTreeSet (и Map) завязаны на Ordering и в случае, если у клиента numeric ID, с этим проблем нету.

Объекты-подписчики.

В случае, если у клиента ID - это число, дальше не читаем, но если это строка, которая может быть довольно длинной, лучше провести некоторые оптимизации. А именно - подсчитать ему статический numeric ID, который намного дешевле сравнивать. По сути, используя BTreeSet с числовым ID, мы получаем некое подобие HashSet, где у объектов хеши кешированы. В HashSet хешами занимается по-умолчанию DefaultHasher, но такое u64-ID довольно сильно подвержено коллизиям, поэтому при совпадении хешей дополнительно вызывается Eq, что в нашем случае недешево, поэтому HashSet и тормозит. У нас в задаче Eq по ID - часто и вредно, ведь мы можем лупить одного и того же клиента в Set довольно много раз.

Берем Sha256, который коллизиям практически не подвержен. В основном, sha-крейты отдают digest в виде [u8; 32], записав объекту статический digest и сравнивая его вместо строкового ID, можно сразу добиться еще 15-20% скорости. Но и это не предел эффективности - если преобразовать digest в массив вида [usize; 32/sizeof(usize)], получаем еще 10% попугаев в плюс.

Параноики могут взять Sha512 и получить еще большую надежность, за счет -5% в скорости. Но на практике в IPC брокерах такой подход избыточен - даже если каким-то чудом два клиента передадут разные IDs с коллизией в Sha256, брокер просто откажет второму в подключении.
👍12👎1
Кстати про бенчмарки. Я как-то спросил у инженеров Beckhoff - почему вы позиционируете TwinCAT тоже как рилтайм IPC, если у вас ADS-вызов идет минимально 5мс?

- Смотри, - говорят. - в один вызов можно запихнуть аж 500 команд в батч. Значит одна команда обрабатывается за 10us. Быстро? Быстро. Яких тебе реформ ще не вистачає?
😁18🔥1

Note that a declared type of "FLOATING POINT" would give INTEGER affinity, not REAL affinity, due to the "INT" at the end of "POINT".


Прекрасное из доки "Datatypes In SQLite"
😁22🔥5
Воюю с CLOCK_MONOTONIC в реалтаймовых приложениях в Linux. Если интересно, вот опции загрузки ядра, которые нашел оптимальные: "isolcpus=2,3,4,5,6,7 clocksource=tsc nohz_full=2,3,4,5,6,7 tsc=reliable". Плюс все вредные левые прерывания - вон с тестовых ядер руками, если это возможно.

Важно. При работе с циклами, которые крутятся с частотами <1ms, мы имеем дело примерно как с ядерной физикой - цикл может изменить своё состояние, если его начать мерять. Поэтому все писькомерки выносим в отдельный поток, желательно на отдельный проц.

По таймерам. В Linux есть tsc, hpet и acpi_pm. acpi_pm - говно, hpet возможно еще не распробовал, сижу, как понятно из опций ядра на tsc. Ядро, естественно реалтаймовое.

Как-то в чате спрашивали, как работает clock_gettime. Это действительно syscall, но работает он через vDSO, поэтому стоит примерно как вызов обычной функции.

Как работает таймер. Прерывания таймера заходят в систему конечно не миллионы раз в секунду, иначе бы любая система сдохла, а намного реже. Так называемые local timer interrupts позволяют APIC послать их на конкретное ядро процессора и ядро обновляет значение времени на каждой голове. Остальные микро и нано секунды считаются на глазок, по циклам процессора, а как только приходит прерывание - корректируются.

Как работает sleep. Растовский thread::sleep (да и остальные), висит на libc::nanosleep, который работает плюс-минус автобус (от одной, если повезет, до 100-150 микросекунд), потому что периодически "будится" всякой всячиной и "досыпает" в циклах, да и никто не знает, когда ядро вернёт вам управление, даже если вы ушли поспать на 1нс. Поэтому все более точные слипы спят какое-то количество секунд (максимальную погрешность nanosleep), а потом докручивают время через спин-луп, гоняя thread::yield_now() или hint::spin_loop(), пока CLOCK_MONOTONIC бежит к нужному значению. Кому более интересно, как оно реализовано, для Rust можно посмотреть например исходники крейта spin_sleep, но в принципе думаю понятно.

Проблема реалтайм-приложения. Во-первых если вы хотите стабильный цикл на микросекунды, оно должно быть рилтайм, иначе какой-нибудь запущенный rustc на соседнем ядре сделает вам офигенный jitter. Реалтайм-приложение забирает весь процессор и не отдает управление никому, включая ядро, пока не освободится. Включая локальные обработчики прерываний.

Вот тут начинается беда. Если риалтайм-приложению нужно уйти в sleep, оно не может просто взять и гонять spin loop - "считалка" ядра относительно бысто собьется, а прерываний времени у нее нет. Можно прокрутить 10 тысяч циклов, можно 30, как повезет, но рано или поздно всё конкретно собьется, пока системный таймер не запушает реальное время на все ядра (а работает он максимум на 1000Hz, если подкрутить), подкрутится а потом собьется опять.

Поэтому технология высокочастотных циклов буквально следующая - цикл обязательно должен иногда спать или ждать i/o. И пока он спит, поток ядра может схватить прерывание от APIC и подстроить свой монотоник. Но тут приходит предыдущая беда - как сказано выше, nanosleep может спать минималкой до 100us а иногда и больше.

Результаты. Для тестов был выбран Intel i7-8550U, потому что был под рукой. На таком процессоре реалтайм-приложение под Linux может гонять реалтаймовый цикл с периодом 150us и максимальным джиттером в 10-15us. Можно гонять быстрее? Да, можно и хорошо. Если не ставить процесс в риалтайм, он будет гонять и 50, и 30us, засыпая в spin loop, но если на соседних ядрах запустится что-то тяжелое, jitter будет примерно непредсказуем.

Это - не годится в промышленную автоматику, пропиетарные конкуренты заявляют о 50-us циклах с джиттером до 10us (правда непонятно как они меряли - без нагрузки соседних голов и я могу). У меня большие надежды на Canonical - в следующем году Ubuntu получает официальное RT-ядро из коробки и ведется неплохая реклама аля "Ubuntu is ready for robotics". Надеюсь, Шаттлворт и пацаны разберутся и с таймерами, а не просто наложат preempt-патч на исходники.
👍20
Нашел совершенно сумасшедший проект. Один чел из Австрии практически в одиночку написал на Rust транслятор с Structured Text в LLVM. https://github.com/PLC-lang

Работа уже титаническая, но это только семечки - этот же товарищ пытается поддерживать всю стандартную библиотеку IEC 61131-3. И тоже на Rust.

Да, я пробовал, я доволен. Даже поражен. Вы берете обычный ST, который пишете либо ручками, либо генерите в Matlab или в CODESYS из FBD/SFC и оно собирает вам, например обычный shared library, который можно в Расте грузить через libloading и гонять PLC program внутри своей рилтайм-запускалки. А запускалка уже можно сказать есть :), в следующем году тоже откроем.

Стандартную библиотеку правда пока не тестировал. Но похоже времена, когда за программирование PLC брали 100500 денег, скоро будут в прошлом.

По сути осталась обратная задача - вменяемо конвертировать LLVM IR в Rust/C, хотя бы из того, что есть. Потому что в реальном мире код PLC иногда подправляется ручками в Visual Studio. Но поскольку решение абсолютно опенсорцное, эти пляски с бубном можно исправлять надстройками.

Beckhoff, спишь?
👍11🔥7
Если у вас нет CLOCK_MONOTONIC, ситуация следующая
😁19👍2🔥1
На одной из моих первых работ нас было человек 30, все до 25 лет и все занимались какой-то херней. Например я писал инсталлятор продукта. То что сегодня заняло бы два дня, у меня заняло 3 месяца. Была версия на bash, perl и даже на m4 (потом выбрали bash) и в таком духе. Также были постоянные митинги, петтинги и прочие по пятницам.

Вдруг к нам пришёл 40-летний кодер и шеф заявил, что тот будет делать отдельный проект, который мы соединим с основным. В то время 40-летний кодер был примерно как сейчас 80-летний - он наверное ещё видел ENIAC и ходил на петтинги с Тьюрингом. Кодер сел за работу, игнорируя митинги, петтинги и даже по пятницам. Через примерно месяцев 5 сдал код, сам написал тех. документацию и свалил куда-то вникуда.
👍45
2025/10/01 08:57:35
Back to Top
HTML Embed Code: