Канал
У Льоні сьогодні
Нотатки архітектора систем та майстра на всі руки. Що я зробив, що з того вийшло, і що це все значить.
-
З Pascal в NumPy
Натрапив на питання про те, як прискорити програму з обчисленнями на Pascal. Чудова задача на оптимізацію, подумав я, та вирішив спробувати NumPy, як топове рішення для обробки математичних даних… з яким я не мав практичного досвіду.
🐍 Перший очевидний результат: наївне переписування на Python уповільнює програму у 160 разів. Очевидний тому, що в Python повільний тип “масив” (насправді “список”) та й взагалі, все що я писав про повільність Ruby, стосується й пітону.
⛴️ Проте так само очевидно, що Python беруть для використання бібліотек на кшталт NumPy. Головною ідеєю тут є векторизація. Так називають два різних підходи. На високому рівні — це виклик функцій низькорівневої мови для обробки цілих масивів замість окремих значень. На низькому рівні — це використання спеціальних інструкцій процесора SIMD: єдина інструкція — багато даних. В разі NumPy обидва підходи мають місце.
🚤 Версія з NumPy за швидкістю близька до оригіналу на Pascal. Що досить непоганий результат, якщо врахувати, що програма написана на Python, де не потрібно оголошувати змінні та можна запускати програму по шматках в Jupyter.
↔️ Єдиним неясним місцем в перекладі було фільтрування масивів. Як виявилось, в NumPy для того є дві семантики:
takeвибирає з масиву за масивом індексів, аextract- за масивом-“маскою”. Але було неочевидно, що функціїfilterнемає. Зате приємно здивувало, що можна прочитати файл з рядками чисел однією функцієюreadfile.🐢 Чому вона не швидше? Для мене відкриттям стало, що NumPy не включає майже ніякого паралелізму. Тільки для окремих операцій — особливо над матрицями. А на цьому прикладі я бачив тільки завантаження одного ядра на 100%. Тобто, в цілому, як програма на Pascal, так і на Python/NumPy робила однакову кількість арифметичних операцій — не дивно, що швидкість приблизно однакова.
🐇 Можливо, десь SIMD чи інші оптимізації й дали прискорення, але з іншого боку, програма на Pascal виділяє памʼять тільки один раз — а на Python нові масиви створюються декілька разів на ітерацію. Що точно дає затримку.
🤔 Залишилося перевірити, якої оптимізації можна було досягнути низькорівневим рішенням.
-
Чим зайнятись на телефоні?
В житті багато моментів, коли в тебе є телефон та декілька хвилин вільного часу. Я не з тих пуристів, які вважають, що будь-яке використання телефону є порочним та в ідеалі потрібно просто стояти в черзі та нудьгувати. Але дійсно, більшість занять з телефоном регресивні: перевірка оновлень по колу, думскролінг, прості іграшки.
Та я б хотів знайти активності (та звички), щоб робити в ті моменти очікування. На читання чи слухання чогось вагомого зазвичай не вистачає фокусу. А ось що поки придумав:
-
🃏 Практикування флешкарток з Anki. Тут навіть за хвилину встигнеш перевірити декілька карток. Можна вчити будь-що… якщо придумати, що, звісно! В мене найбільший успіх був з 5000 найчастішими словами німецької. Але технічно можна пристосувати від стандартної бібліотеки C до імен та світлин колег.
-
💭 Скид думок. Просто відкриваєш нотатку та записуєш, що в голові. Це взагалі хороша звичка, а тут вона дається мінімальними зусиллями. Можна навіть надиктовувати. В ідеалі ще не забути переглянути ті нотатки… але й без того скид думок — гарна терапія.
-
🖋️ Як варіант скиду думок — запис чернеток до постів, проєктів, віршів, романів, чого там у вас.
-
🪷 Ну й нарешті, якщо оточення дозволяє, можна й прибрати телефон та побути в моменті. А допоможе в цьому наш спонсор на сьогодні Headsp… жартую!
-
-
SwiftUI на macOS: спостереження
За тиждень неквапливого програмування зробив собі на SwiftUI застосуночок для ведення списків по GTD. Декілька спостережень та висновків:
-
Дуже приємно працювати, коли дані сидять в JSON файлі. Резервне копіювання автоматично вже відбувається — як і для всіх моїх файлів. Міграції можна зробити хоч вручну. Експорт вже готовий. Імпорт нескладно зробити, може теж вручну.
-
Порівняно з вебом, зовсім немає “бекенду/фронтенду”. Це теж спрощує уявлення. Всі дії є безпосередніми: ніяких HTTP API.
-
Observation все ж дуже гарно працює. Будь-який клас з
@Observableстає моделлю та долучається до оновлення виглядів. It just works! Причому ми вільні використовувати функції та проміжні змінні. Головне, щоб під час промальовки було звернення до властивостей об’єкта з@Observable. (Або навпаки — якщо щось закешуємо та звернень не буде — то перемальовки теж.) -
Також до всього
@Observableможна додати@Bindableта писати дані прямо в модель. Це мінімізує потреби в локальному стані; він зберігає тільки дійсно локальні змінні як видимість діалогів, фокус полів тощо. -
SwiftUI взагалі теж приємний фреймворк, якщо звикнути до його ідіосинкразій. Головне навчитися рефакторити та різати компоненти. Порівняно з вебом, практично не потрібно “писати CSS”, тільки дещо коригувати те що є. З іншого боку… такого чистого аркуша, як у вебі, теж певно ніколи не буде. Але це більше задачка для дизайнерів.
🦀 Нарешті, щоб було веселіше, залишу вас зі статтею: Swift is a more convenient Rust.
-
-
Docker як архівна система
На початку року вже писав, як використати Docker, щоб втекти з власного оточення в таке, що є зручним. Сьогодні нарешті випробував окремий варіант такого використання: повернення в історичне оточення.
Ситуація знайома до болю. Стикнувся з проєктом на JavaScript, який вже років 5 не підтримувався. Node.JS 12 - старіша за епоху Apple Silicon та потребує Python 2 для встановлення. А з нею - Node-Sass, Webpack 4 і все інше. На моїй машині все це не запускалося.
Зазвичай я намагаюся привести такий проєкт до життя — оновити залежності, причепурити. Але на цей раз проєкт не мій, а мені тільки декілька рядків виправити, зібрати та забути. Я вже майже змирився з тим, щоб перекласти задачу на супроводжувача, але тут згадав про Docker та про свій досвід запуску команд.
Знайшов на Docker Hub образи від Bitnami. Запустив оболонку, та, дуже несподівано, всі команди від
npm installдоnpm buildпройшли як по маслу!docker run -v .:/mnt -it bitnami/node:12 /bin/bashА взагалі в ідеалі можна було заздалегідь підготувати образ для підтримки, навіть зібрати його та зберегти. Навіть разом з усіма залежностями, базами, утилітами та всім іншим.
-
Дизайн даних для інженерів
Мені як інженеру багато разів доводилося виводити дані… так би мовити, без дизайну. Зазвичай для внутрішніх потреб: нечасто дизайн покриває всілякі адміністративні панелі. Тому, гадаю, перша дисципліна дизайну для інженерів — то подання даних.
Щоб просто показати табличку, чи перелік, чи форму, багато думати не потрібно. Проте тут є аксіома/порада: будь-які дані можна подати в різному вигляді та завжди можна знайти щось краще, ніж вигляд за замовчуванням.
-
📰 Форматування. Взяти, наприклад, відбиток часу. Показати його в ISO 8601? Чи може тільки дату? Чи взагалі як відносний час? Число — з якою точністю? Рядок — як є чи скоротити частину, що повторюється у всіх записів?
-
🍱 Групування. Всякі дані мають внутрішню структуру. Можна зробити їх легшими до споживання, якщо розбити на логічні групи. Для того потрібно зрозуміти, на що буде дивитися читач. До того ж група дозволяє написати повторюване значення тільки один раз.
-
📈 Похідні. Часто важливіше бачити обчислені дані: відсоток, середнє, тривалість — а не сирі дані: кількість, початок та кінець проміжку. Також інколи можна обчислити корисний статус чи категорію (якщо такі цікаві читачу.)
-
💬 Назви замість ідентифікаторів. (Або навіть посилання, якщо є куди.) Звісно, в сирих даних частіше будуть ідентифікатори, проте для читача вони рідко наділені змістом.
-
🎨 Оформлення. Крапля кольору або емодзі багато роблять для сприйняття інформації. Так само як і корекція відступів, розміру шрифту і таке інше, але то вже заходить на територію Великого Дизайну.
-
✂️ Приховування неважливого. Менше = легше. Окрім того, щоб прибрати зовсім, можна сховати дані за підказкою або стиснути у вигляді (емодзі замість текстового статусу) або поєднати два поля в одне (назву та посилання).
Практикуватися в дизайні можна на адмінпанелях, звітах… навіть на записах в журналі та вирізках для слаку! Та від того повсякдення стане трохи зручніше та ефективніше.
-
-
Бібліотека samber/lo: функціональні ідіоми для Golang
Випробував таку нову (для мене) бібліотеку - samber/lo. Знаєте пакет Lodash для JavaScript? Там, де купа зручних функцій для програмування у функціональному стилю? От це таке саме тільки для Go.
Бібліотека вийшла у 2022, після того, як в Go 1.18 зʼявилися дженерики. Та, завдяки дженерикам, вона є швидкою та безпечною.
Та вона дійсно дозволяє писати функціональний код на Go. Наприклад, ось обчислення першого року публікації кожного автора:
lo.MapValues( lo.GroupBy(books, func(b Book) string { return book.Author }), func(bb []Book) int { return lo.Earliest(lo.Map(bb, func(b Book) int { return book.year })) }, )Написав приклад та подумав, що імперативне рішення може було б і ясніше. Не впевнений, що варто робити такі багаторівневі конструкції. З іншого боку, виклики в один рядок на кшталт
FlattenчиGroupBy, чиFilterздатні замінити багатомовні цикли та зробити код ясніше. А ще тут є функціяMust, якої теж постійно не вистачає, щоб висловити “якщо тут буде помилка, то я зовсім не знаю, що з нею робити”.Також зазначу, що хоч дженерики здатні виводити типи — тому їх писати не потрібно — але функції потребують явного задання типів. Тому компактне в JS
book => book.yearперетворюється наfunc(book Book) int { return book.year. Та це помітно псує код.Щодо ціни всіх цих абстракцій… Роб Пайк дозволив про це не думати. Я раджу всім спробувати у своїх проєктах цю бібліотеку та думаю, вона знайде свою нішу в вашому коді.
-
Робочий час та календарний час
Робочий час — той, що ми витратимо безпосередньо на виконання роботи. Це те, про що зазвичай думають при плануванні часу. Але окрім нього є ще…
Календарний час — той, що мусить минути для завершення роботи. Та від нас не залежить. Наприклад: від створення пул-реквесту до його появи на продакшні може минути тиждень. Залежно від процесів, звісно.
Або інший приклад: якщо для побудови моделі нам потрібні три місяці даних, то доведеться чекати. Або поки наступить день бронювання. Або поки поштою щось приїде.
Таке розрізнення стане корисним під час планування. Хоча “календарні” витрати часто займають набагато довше — зате проходять без нашої участі. Особливо по адмініструванню зустрічаю багато задач, де роботи на пів години, потім чекати декілька днів, потім ще пів години.
Чим раніше почнемо “календарну” задачу — тим раніше вона закінчиться. Це теж варто враховувати у плануванні. Трапляється що робота починається з налаштування збору метрик чи додаткових журналів. Поки доробляємо решту — вже буде на що подивитись. А якщо не подумали та почали збір пізніше — доведеться чекати.
А ще “календарні” задачі можна робити паралельно. Теж якщо вірно розпланувати та помітити наявність затримок. Як частий приклад, PR-потяг (це коли наступний PR роблять до попереднього, а не чекають коли той приймуть.)
-
SwiftUI та найпростіша модель даних для нього
Сьогодні зненацька зробив 50% мікрозастосунку для GTD (ой, лишенько.) Здебільшого отримував задоволення, хоча розробка зі SwiftUI це американські гірки від “як тут легко зробити Х!” до “ну чому Y абсолютно не піддається логіці?” До речі, писав у SweetPad - це доповнення для VSCode від друга каналу Євгена майже заміняє XCode.
Оскільки хотілося якнайшвидше перейти до бізнес-логіки, спробував таку модель даних: ніякої бази, просто класи, які я зберігаю у JSON.
Завдяки фреймворку Observation, Ui впевнено реагує на зміни даних. Є ще модифікатор
@Bindable, яким можна перетворити ті ж класи на привʼязки для текстових полів та інших місць, які потребують редагування. Тобто фактично одним класомAppStateможна покрити всі потреби застосунку в стані. Зрозуміло, що коли даних буде багато, така “база даних” стане повільною, але для керування власними задачами точно вистачить.Також поки свідомо відмовився від синхронізації. Взагалі з рішеннями для синхронізації поки більше питань, ніж відповідей. Для мене головним є стабільна робота застосунку локально на десктопі. Поки всі рішення, що я бачив, цю стабільність порушують — зокрема, в мене поламався пошук у Reminders.app - тобто й Apple не може на власній платформі зробити надійну синхронізацію.
-
Розгортування в хмару, чому ти таке повільне?
Величезним мінусом всілякої хмари, порівняно з розгортуванням на конкретні машини, було те, як воно до біса повільно розгортується. Як з Heroku почалося -надцять років тому, так і зараз тільки гірше стало.
Повільно все тому, що ми не володіємо в хмарі ресурсами. Це як якби щоразу перед тим як їхати кудись на машині, потрібно було її взяти в оренду. І не зручним Гетьманкаром, а через заповнення документів та пошук машини на стоянці.
Відповідно, хмарний сервіс повинен знайти для нас потужності, потім завантажити наш код (або величезний образ Docker), запустити, пройти перевірки, зупинити старі контейнери. Heroku взагалі чудову абстракцію побудували, коли знаєш, що всередині.
А до того ще CI теж повільний. Теж збирає залежності. А якщо є кеш, то і його буде звідкілясь завантажувати. Підготовка може тривати половину часу, що робить паралельний запуск мало корисним (бо звісно ж, кожний паралельний потік буде все те ж саме робити окремо, на власній машині чи контейнері.)
Де це особливо обурює, так це на стейджингу. В мене є окремий чекліст для перевірки коду на стейджингу, бо поки моя гілка розгорнеться, я точно про неї забуду. Бо не буду я 10% робочого дня дивитися, як CI стягує кеш, чи CD перевіряє хелс-чеки.
-
Список для читання через два роки
📖 Списком для читання я користуюся практично щодня. Через те мені не потрібна сотня вкладок.
⏲️ Список для читання гарно працює, коли нічого в ньому не має терміновості. Раніше я користувався ним як буфером для всього, що не зможу закінчити зараз — статті, відео, товари, документація. Але, коли список розростається (а так і буде), то все орієнтоване на задачі в ньому губитиметься.
📝 Тому тепер, коли зустрічаю сторінку, яка не просто цікава, а саме зараз, то записую її в Drafts, де побачу (та оброблю) протягом тижня. (До речі, що в Reeder, що в Drafts, та багато куди ще легко зберігати через функцію “Поділитися” на macOS та iOS.)
🪨 А якщо вона стосується поточного проєкту, то йде в Obsidian, у відповідне місце. Тут дуже задоволений, що таке місце зʼявилося, та ще й можна поруч ілюстрації зберігати. (В Obsidian у мене є як сторінки “інформаційні”, тобто позачасові, так і “проєктні”.)
📺 Також нещодавно почав “список чого подивитись” - просто в Reminders - та так само як список для читання, він допомагає в зручний момент дивитись щось цікаве, а не просто випадкову “піну” з YouTube.
🔎 Ще натрапив на таку зручну функцію, як попередній перегляд посилань в браузері Orion. Вона працює поза браузером та відкриває посилання в тимчасовому вікні — яке за бажанням можна “розгорнути” у звичайну вкладку. Це дуже спрощує перегляд посилань з Obsidian чи з Slack - браузер не захаращується випадковими вкладками.