Стендап Сьогодні
Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті
Підписатись на RSS
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
24.02.2024
Водяне охолодження ПК — що воно таке?
В мене вже третій рік ПК працює на водяному охолодженні. Весь цей час збираюся щось написати, отже, сьогодні почну.
Мій ігровий ПК стоїть в зачиненій шафці під телевізором — в режимі HTPC - проте це потужний ігровий компʼютер. З самого початку йому не вистачало охолодження. Скільки вентиляторів не додавай в корпус компʼютера, шафка все одно перетворювалась на духовку. Можна було б придумати повітрообмінник з шафи назовні, але, після довгих роздумів, я все ж вирішив піти з водяною системою.
Що ж, водяне охолодження безперечно здатне перетворити теплові потреби компʼютера на щось абсолютно нове. Я обрав зовнішній радіатор — бо задача була винести тепло за межі шафки. Таке рішення працює абсолютно беззвучно більшість часу, та набагато тихіше звичайного охолодження під час найбільш навантаженої роботи: теплова маса радіатора дає величезну фору будь-якій іншій системі охолодження.
На мою думку, водяне охолодження варто своїх коштів… але кошти підуть немаленькі. Історій про фітинги, які протікають, трубки, які розкладаються, і так далі, достатньо, щоб зрозуміти, що заощаджувати не варто. Навпаки, компоненти від надійних виробників служать довго та абсолютно без проблем. Орієнтовно, я б закладав бюджет на систему у $500 - $1000.
Окрім витрат, доведеться мати базово-просунуті навички в цілій низці галузей. Розбирати компʼютер — очевидно. Але також, зняти радіатор з відеокарти (це зовсім не така типова задача, як з процесора!). Зібрати “сантехніку” та перевірити на протікання. З зовнішньою системою — подовжувати кабелі для вентиляторів. Вішати все це на стіну. А потім ще й налаштувати програмну частину, щоб керувати швидкістю помпи та вентиляторів (бо так, є ще й помпа зі своєю фізикою).
Одним словом, хобі як дороге, так і складне. Але принаймні результат того вартий. По-перше, це красиво та рідкісно. По-друге, дозволяє не згадувати про охолодження навіть у найвибагливіших іграх. Поки на цьому все, подробиці будуть пізніше.
23.02.2024
10 днів зі стохастичним таймтрекером
Вже трохи більше як тиждень користуюся своїм таймтрекером (який паралельно дописую). Коли виправив надсилання сповіщень, то, фактично, MVP вже був готовий. Нагадаю, що на відміну від типового трекера, цей — стохастичний — самостійно опитує (“пінгує”) тебе у випадкові моменти часу — так само як це роблять статистичні профайлери коду. В середньому, раз на 45 хвилин, але на практиці бачив і раз в 3 хвилини, і 3 години без пінгів.
-
Заносити записи легко, навіть без застосунку на Apple Watch. Поки є тільки застосунок на телефон, але випадків, коли телефона немає поруч, а можливість скористатися годинником є, дуже мало. На практиці я встигаю записати понад 90% записів. Якщо відразу не запишеш, потім часто це стає неможливим, оскільки…
-
Цей трекер потребує заносити те, чим займаєшся зараз. Якщо я пишу пост, а потім перемкнувся на новини, та у цей момент прийшов пінг трекера — запишу “читав новини”. Виходить, за винятком дійсно безперервних занять, через десять хвилин точні дані вже не згадаєш.
-
CтохастичнийТаймтрекер потребує міцної віри у статистику. З простого погляду, він виглядає нечесно, бо то пропускає важливі заняття (годину стояв в тягнучці, пінгів не було) або навпаки, перебільшує дрібні (три пінги за 10 хвилин вмивання). Тому треба памʼятати, що статистично цей метод є абсолютно чесним, більш чесним, ніж коли заносиш час на свій розсуд — бо в такому разі обминаєш “дрібні” або просто неприємні відрізки (такі, як перегляд новин під час “написання посту”.) А статистичний трекер навпаки привертає до таких “дрібниць” увагу — якщо я частіше “читаю новини” ніж “пишу пост”, то це вже привід задуматись над витратами часу.
-
Вплив на свідомість: відсутність передбачуваності пінгів та потреби власноруч заносити час призводить (може призвести) до загальної підвищеної усвідомленості — не заради трекера, а заради себе. Коли памʼятаєш, що в будь-який момент може прийти пінг та доведеться записати “читаю новини”, то не так вже й хочеться багато часу на те витрачати. (Але, також буває прикро, коли три години працюєш невпинно, а пінгів немає. Не пощастило. Але не в пінгах сенс.)
Поки експеримент мені подобається, далі буде.
22.02.2024
Проблема декількох версій залежностей
Є така проблемка в проєктах на Go, що у транзитивних залежностях може бути декілька версій однієї бібліотеки. Ну, наприклад, github.com/redis/go-redis/v9
та github.com/go-redis/redis/v8
. Окрім того, що це збільшує розмір програми (та хто його лічить?) - є й ризик помилки.
(До речі, в Go предивна система версій модулів. Взагалі в Go модуль це тека. Ніякої “збірки” у .gem, .jar, чи навіть .zip немає. Назва модуля дорівнює його URL в Git. А наступна несумісна версія модуля створюється… не в теці v2
, як може це здатись, а у гілці з назвою v2
. При цьому назва модуля залишається такою самою. Мені доводилося це робити з нашою бібліотекою go-global, коли вона переїхала з AWS SDK v1 на AWS SDK v2, який цій системі не слідує.)
Коли ти оперуєш типами з пакета, легко помітити різницю між версіями, бо вони є несумісними. Але не всі операції потребують типів. Наприклад, константи помилок з різних версій пакета не проходитимуть перевірку на рівність. Та й інші нетипізовані константи. А ще глобальні налаштування на кшталт хуків — вони теж можуть піти не туди, куди треба.
Одним словом, коли значення, які за всією логікою мають збігатись, все ж розрізняються, перше, що спадає мені на думку — перевірити версії модулів.
Така сама тільки гірше проблема в JavaScript - бо там типи не допомагають, а залежностей експоненційно більше.
21.02.2024
Пара неочевидних ідіом у SwiftUI
Взагалі, помітив, що останнім часом багато натрапляю на прості, ідіоматичні рішення замість своїх ускладнених за браком знань. Вчусь, напевно. Бо буває, що ідіоматичний код очевидний, а буває навпаки, перелопатиш половину Інтернету, але на жаль не за тими пошуковими запитами.
-
Для відображення модального вікна вживається метод .sheet(isPresented:). (Це “модифікатор”, має схожу семантику до HOC у React, тобто надає компоненті нових властивостей, в цьому разі — можливість показати модал.) Типовий сценарій — модал редагування — потребує також вибору елементу, який зʼявиться в модалі. Тобто потрібно було мати дві змінні стану — видимість та елемент — та ще й синхронізувати їх поведінку. Дізнався, що є й інша форма цього методу: .sheet(item: $itemToEdit). Тут модал стане видимим, коли
$itemToEdit != nil
, та автоматично присвоїть їйnil
при закритті вікна (Синтаксис з доларом$
- це “привʼязка” - тобто спосіб передати посилання на значення, здатне на запис.) -
Є такий компонент
List
, який виводить всілякі списки (Більша частина інтерфейсу iOS - списки різного роду. Форми це теж списки, до речі. ) Список може бути пласким, може — секційним. Але що, коли треба показати ієрархічний список — наприклад, файлову систему? В інтернеті знаходив багато рішень, від вкладенихList
до обчислення вірних відступів за горизонталлю. Проте сам компонентList
має режим з вкладеністю. Якщо передати аргумент children. Як це працює — бо “дітей” треба буде брати у кожного елементу, та ще й з вкладеністю? Уchildren
ми передаємо шлях атрибута (key-path), за якимList
зможе знайти дітей кожного конкретного елементу. Досить екзотична фішка Swift, як на мене.
20.02.2024
Операції з часом в OpenSearch
Поступово дізнаюся нові можливості OpenSearch (та ElasticSearch), які, хоч і базові, але чомусь на поверхні не лежать. Сьогодні — про те, що дати набагато гнучкіше, ніж я собі думав.
Взагалі OpenSearch зберігає дати як числа. Проте, як я розумію з коду, різниця в індексації є: як я писав, числа OpenSearch індексує деревом множин, щоб швидше шукати за діапазоном. Для дат ці множини будуються не аби як, а за одиницями часу: хвилинами, годинами й так далі: ось код. Звісно, це саме те, чого ми хочемо від пошуку за часом.
Але це таке, знати не потрібно. А що варто знати, так це те, що час можна вказати зі скругленням до потрібної одиниці. Наприклад, потрібно мені знайти записи за останній тиждень. Як вказати верхню межу? Відома проблема, що просто датою не можна — відбитки часу за останню дату будуть технічно більші за дату без часу. Тому можна вдаватись до хаків: написати 23:59:59
(і втратити відбитки за останню хвилину!), чи зробити строго менше наступної дати. Проте в OpenSearch можна просто зробити запит зі скругленням до дня:
{
"range": {
"timestamp": {
"gte": "2024-02-12||/d",
"lte": "2024-02-18||/d"
}
}
}
(Ба більше, в цьому прикладі можна було б скруглити до тижня та ще спростити собі роботу.)
Та й повертати значення дати можна відразу в такому форматі, як нам потрібно: для документів з пошуку є опція docvalue_fields
(дивна назва), а для агрегацій за датою просто format
. Трохи незвично таке робити в базі, бо я звик, що бібліотека-клієнт віддає обʼєкти дати, з якими вже робиш що хочеш — проте за відсутністю такої бібліотеки дуже зручно.
19.02.2024
Вебсервери на Ruby: чим вони відрізняються
Хотів в завершення теми про рівночасне програмування пройтися по серверах на Ruby та порівняти їх. Виявилось… що тема трохи застаріла. Памʼятаю часи, коли вибір сервера був гарячою темою — років з 10, а то й 15 тому. Але наразі все стабільно (та й Ruby перестав бути такою гарячою темою.) Отже.
-
Puma на цей час еталонний вибір. Заслужила місце в стандартному пакеті Rails, а це багато значить. Puma є як багатопотоковою, так і багатопроцесовою бібліотекою, тож все, про що я писав останні дні, до неї застосовується.
-
Webrick - офіційний сервер Ruby - є однопотоковим. Через це обробляє запити лише по черзі — навіть поки один з запитів чекає, скажімо, на інформацію з бази, у Webrick немає можливості взяти інший — тому процесор просто марнує час. Перевага Webrick у реалізації чисто на Ruby, а також у модульності: я використовував Webrick для інтеграції у застосунок проксі-сервера.
-
Unicorn колись був вибором №1, бо першим навчився робити декілька процесів та керувати ними — таким чином досягнувши паралелізму. Але всередині процесу був тільки один потік, тому Puma обʼєктивно виграє у швидкодії. Перевагою Unicorn є надійність, причому реальна: навіть критична помилка в коді призведе до відмови тільки в конкретному запиті, далі процес буде перезапущений. А в Puma якщо один з потоків ламається, наприклад, зависає (а таке трапляється й досі!) - то всі інші запити з цього процесу теж ламаються. Проте наразі Unicorn не підтримується, а в Puma за бажанням можна обмежити 1 потік на процес.
-
Thin - відрізняється використанням EventMachine - однопотокової бібліотеки для обробки вводу/виводу. За архітектурою вона схожа на JavaScript (який теж однопотоковий). Але, на відміну від JavaScript, для отримання переваг рівночасності наш код повинний був використовувати бібліотеки вводу/виводу з підтримкою EventMachine. В контексті вебсервера такий підхід втратив популярність, проте є багато популярних бібліотек, які й досі використовують EventMachine.
-
Практично у всіх серверів є парсер на C. Ну, бо так обʼєктивно швидше. JIT це гарно, але надійніше один раз переписати вузьке місце, ніж сподіватись на вдачу. Але, прошу помітити, всього на С ніхто не переписує.
НДНЧ: Puma - це все, що тобі потрібно.
18.02.2024
Рівночасне та паралельне програмування
Зі всіма цими написами про рівночасність та паралельність легко заплутатись. Що я не раз зустрічав у плануванні алгоритмів та підходів. (До речі, правильний український термін - рівночасність, а не одночасність, як я писав раніше. Жодний з них не є самоочевидним, на жаль.)
Рівночасність (concurrency) - це здатність програми утримувати декілька потоків виконання. Побутова аналогія: я можу “рівночасно” робити роботу та збирати Лего. Поки працюю — Лего не збирається. Потім припиняю роботу — сідаю за конструктор. Як робота, так і конструктор продовжуються з того місця, де я зупинився. Це рівночасні процеси. Але я не можу збирати в той самий час, як працюю. Це вже…
Паралелізм (parallelism) - це здатність виконувати більш ніж один потік у момент часу. Паралелізм потребує, в першу чергу, більше одного ядра процесора. Бо тут криється головний нюанс: йдеться про виконання процесором, не очікування на зовнішні ресурси. Очікування на ресурси є апріорі “паралельним”, навіть коли “паралелізм” нам недоступний — наприклад, з одним ядром процесора чи в мові з GIL.
Якщо продовжити аналогію, то поки замовлений конструктор тільки їде поштою, нічого не заважає мені працювати. А ось зібрати його в той самий час як я працюю, може тільки дружина — це вже справжній паралелізм!
Хиба в плануванні рівночасних програм полягає в відсутності розрізнення роботи процесора та вводу/виводу. Коли програма здебільшого зайнята вводом/виводом — здатність до паралелізму не важлива — ані за властивостями мови, ані за кількістю ядер процесора. А якщо потрібно прискорити обчислення — тоді треба шукати паралелізм.
17.02.2024
Навіщо потрібен GIL (глобальне блокування інтерпретатору)
Ще трохи розбирався з паралельним виконанням в Ruby. Хотів зрозуміти, чому в Ruby є GIL. Дізнався, що нічого в характері мови не потребує глобального блокування як такого. GIL це архітектурне рішення.
В будь-якій програмі будь-якою мовою з одночасністю виникають проблеми псування памʼяті через паралельний доступ. Байдуже, динамічна мова, чи інтерпретована, чи статична, чи використовує віртуальну машину. Щоб злагодити доступ, використовують різні механізми блокування. Навіть якщо код застосунку блокувань не містить, то системні підпрограми обовʼязково.
GIL - це найпростіша форма блокування. З GIL можна практично не згадувати про паралельний доступ в коді інтерпретатора. Проте, на жаль, мова втрачає всяку можливість паралельної роботи взагалі. Це не так страшно насправді оскільки головною задачею одночасного виконання є ефективна обробка вводу-виводу, а не паралельне використання ядер процесора.
Чому розробники інтерпретаторів йшли на такий компроміс? Як я розумію, причина суто історична. Інтерпретатори з GIL (Ruby MRI, CPython) зʼявилися ще у девʼяності, коли багатоядерних процесорів взагалі не існувало. Пізніше додати в код систему детальних блокувань практично нереально, тому й зробили просте глобальне блокування.
А є й популярні мови PHP та JavaScript - які взагалі не мають моделі паралельного виконання. Немає там можливості створити потік. І це їм не заважає бути одними з найпоширеніших мов на сервері.
16.02.2024
TimeTag - інтервал впевненості
Перейшов потроху зі збору даних (який працює досить непогано!) до аналізу. Тут не все просто: оскільки тайм трекер питає тебе в випадкові моменти, то не можна просто підрахувати розподіл результатів. На додаток маємо обчислити інтервал впевненості: “цією міткою відмічено 2 години плюс мінус 30 хвилин”.
Шляхом реверс-інжинірінгу алгоритму та пошуку в інтернеті (я в статистиці нічого не розумію) знайшов купу різних формул. Проблема тут така, що одні терміни спираються на інші, і так далі, та часто взагалі не зрозуміло, чи рухаєшся в правильному напрямку.
Нарешті, знайшов як сформулювати питання та знайшов таке саме на StackOverflow. Яке ж було моє здивування, коли помітив, що питання поставив оригінальний автор алгоритму! На щастя, він знайшов правильну формулу для інтервалу впевненості, та ще й саме для релевантної задачі.
Є тільки одна маленька проблема… формула використовує обернену гамма-функцію (TIL.) У автора вона написана в пакеті Mathematica. А як мені обчислити таку в коді? Спочатку знайшов npm-пакет, та навіть почав перекладати на Swift. Проте, перекладати складні математичні функції без розуміння ну дуже ризиковано — там добра тисяча рядків коду. Та й де гарантії, що автор невідомого NPM-пакету не встиг вже наробити помилок?
Тоді згадав про найстаріший трюк математичного програмування — обчислення за таблицею. Все, що мені потрібно — це знати значення оберненої гамма-функції для можливих значень кількості моментів з міткою. А значення можна взяти в Mathematica, точніше, у Wolfram Alpha. Причому відразу масивом — безплатно дає до 2 тисяч значень за раз, більше не проходить за часом:
InverseGammaRegularized[Range[8001,10000],0.025]
Для 10000 моментів інтервал впевненості дорівнює 4% - я думаю, що похибка “вимірювання” буде більша, тож немає сенсу збільшувати математичну точність. А зберегти в коді 20000 значень незрівнянно легше (та швидше у виконанні!), ніж обчислювати їх. Колись таким чином ігри зберігали тригонометричні функції та таке інше.
15.02.2024
Виправив підписку на Twitter через RSS
Як я дуже давно писав, для стеження за джерелами інформації використовую RSSHub. От є тільки невеличка проблемка — з Twitter останнім часом щось… трохи сталося та стрічки вже з пів року не працювали. Нарешті виправив.
Взагалі зараз для отримання постів потрібна авторизація. Але й з нею є різні дивні обмеження. Через це, наприклад, відомий альтернативний фронтенд Nitter, скоріше за все, припиняє існування.
Останнім часом Nitter використовував ось такий кривий хак: після встановлення застосунку Twitter на Android тобі видавався тимчасовий гостьовий акаунт. Ось цей акаунт вони й використовували — в сукупності з клієнтськими ключами того ж застосунку — для доступу через внутрішній API, на котрому побудовані офіційні застосунки. Такі відчайдушні рішення довго не тривають — особливо на публічному сервісі, яким був Nitter. Так і стало — гостьові акаунти більше не видають.
А для моєї особистої копії RSSHub поки працює такий саме підхід, тільки вже з особистим акаунтом. Для того дехто написав скрипт, яким можна “зайти в застосунок Android” та отримати ключі. Навіть не потрібно створювати свій проєкт для доступу до API. Далі ключі додаються в RSSHub та — о диво — стрічки знову працюють. Тільки чи надовго?