inv2004 Dev Blog
309 subscribers
76 photos
4 videos
75 links
Он всегда был не прочь подкрепиться. Кроме того, он был поэт
Download Telegram
Ох Rust

Метод просто элементы итератора печатает - Несколько часов пробирания через то как избавиться от владения итератором:

impl PlayerObj where
for<'a> &'a T: IntoIterator,
U: std::fmt::Debug
{
fn print(&self) {
self.data.into_iter().for_each(|x| println!("{:?}", x));
}
}

#rust #old
KX25

Случайно забежал на kx25 https://kx.com/kx25/ . Если отбросить 90% рекламной болтовни, то впечатления следующие: kx продолжает попытки расшириться, штат ~800 человек, причём, если вдруг у кого желание поучаствовать, то, вероятно, уровень входа не особо высокий, так как год назад, на прошлом kx-meetup, про kdb рассказывал человек из kx, по первому впечатлению, совсем студенческого уровня.

Из презентаций показалось, что kdb, в большинстве случаев, используется как некий продвинутый, конечно извиняюсь за такое сравнение, VBA и excel, что немного печально. В основном то, что показали, имеет наколенный или демонстрационный вид, фреймворки не упоминались вообще, вероятно те, у кого большая кодовая база kdb, пишут всю инфраструктуру с нуля, но, так как писать с нуля тяжело и долго, то в большинстве случаев kdb предпочитают использовать в связке с python.

Долго лавировал в толпе в галстуках, пытаясь опознать Arthur Whitney, но, в итоге, обнаружил его в более простой, если можно так сказать, форме, что приятно. Это не отменяет того, что он был постоянно окружён небольшой говорящей толпой, или же пытался убежать от неё, так что переговорить удалось всего парой слов.

Зато удалось интересно пообщаться с core частью kx - Pierre Kovalev и Oleg Finkelshteyn. Подтвердились мысли о том, что создание языка Q преследовало в основном коммерческие цели, и оказалось что я не один, кто считает K понятнее и проще чем Q. В Q 3.6 до сих пор используется K4, однако оказывается Артур уже делает K8 для себя, к сожалению, как и K5 два года назад, не думаю что её удастся посмотреть. Интересно, что K8 не только не пополнела, но и сокращается в объёме относительно K4.

Поспрашивал были ли попытки прикрутить подобие хоть какой-то статической типизации, так как, моё мнение, что как только количество кода или количество человек в команде переходит какой-то порог, то динамическая типизация kdb превращается в ад, что я и наблюдаю на работе, но похоже таких попыток не было, так как Pierre конечно щупал haskell, но о типизации в целом негативно отзывался. Так что, вероятно, если снова дойдут руку, я попытаюсь ещё раз накрыть это системой типов, чтобы хотя бы посмотреть что получится.

Из сопутствующего - было очень много Machine-Learning, но, понятно что через питон или биндинги, это отчасти доказывает, что kdb хорош как небольшая обвязка, но что-то больше писать на этом врядли легко, и не только из-за лицензии. Троль Peter Wang из anaconda.org, предал анафеме C++, однако, как обычно, что на замену не сказал, так как очевидно что не python, так как в докладе он был основной темой. Я спросил его про Rust и попросил в будущем поделиться впечатлением.

#kdb #old
Продолжаю бенчмаркать

Итого за три дня написан ~ одинаковый функционал на python и type-script, который уже давно был реализован на расте. Задача довольно примитивная, но зато из реальности:
1) есть какое-то количество данных.
2) строим 100 минутных свечей (по сути только close) _от_текущего_тика_ (есть ли в этом смысл - это отдельная тема)
3) загоняем в ML
3) делаем #2 и #3 проигрывая исторические данные в цикле, например от 0 до 2000 для начала.

Итого:

PYTHON: Изначально всё было написано на питоне и pandas, который вроде как раз предназначен для подобных группировок, однако именно группировку он выполнял настолько долго, что я это даже не мерял - больше минуты.
TS: Написал это же на TS с использованием data-forge - картина аналогичная - с точки зрения производительности всё настолько печально, что даже замерять нету смысла.
TS#2: Выкинул data-forge, итого - TS разогнался до ~20 секунд.
PYTHON#2: Выкинул pandas и переписал всё исключительно цифрами (заодно время тоже, до этого это был datetime) на numpy - получилось всего 0.5sec.
TS#3: Выкинул модуль moment.js и TS тут же разогнался с ~20 до ~2 секунд, что в целом неплохо, учитывая что никаких биндингов.

На данном этапе python победил своим numpy, хотя на тот момент TS нравился больше.

Следующий этап - решил подключить ML, ведь каждый хочет с этим поиграться.
PYTHON#3: 20Mb модель он считает ~13ms.
TS#4: Похоже на то что keras.js это имплементация ML без всяких биндингов - следовательно всё считается на ванильном JS. И, оказалось, что в таком виде, 20Mb модель он предиктит 900ms, что при любом раскладе неприемлемо. Учитывая, что он это делает асинхронно в одном потоке, то, если продолжать слать данные, цифры модели вообще могут начать приходить с большим >30сек. опозданием.

Некий, наверное очевидный, вывод:

Python тормоз, на нём писать нельзя, но можно использовать волшебный numpy, если задача хоть как-то в него заходит.

TS - вроде заметно быстрее Python, но, из-за отсутствия биндингов, в конечном итоге он проседает сильнее.

Итого: для склейки оставляю питон.

#python #js #bench #old
inv2004 Dev Blog
Продолжаю бенчмаркать Итого за три дня написан ~ одинаковый функционал на python и type-script, который уже давно был реализован на расте. Задача довольно примитивная, но зато из реальности: 1) есть какое-то количество данных. 2) строим 100 минутных свечей…
— update —
Добавил немного логики в код, и это сразу затормозило питон раз в 10. Аналогичное происходит и в kdb - быстро работают только высокоуровневые операции. Так что решил ещё раз pypy попробовать:

Итого:
numpy не собрался, но я поставил из собранного wheel.
scipy собрался.
Затык произошёл на keras, он не смог собрать h5py под pypy.
Rust. The Computer Language Benchmarks Game

Что-то где-то обновилось, пересчиталось, и я, неожиданно для себя, вылез на первое место по Rust в k-nucleotide benchmark, причём даже с небольшим заделом:

https://benchmarksgame-team.pages.debian.net/benchmarksgame/performance/knucleotide.html

1. C++ g++ #2
3.66 155,956 1624 11.16 70% 98% 70% 68%
2. C gcc
5.07 130,008 1506 15.25 88% 84% 58% 73%
3. C# .NET Core #9
5.29 186,892 2574 17.90 96% 67% 93% 84%
4. Rust #8
5.76 135,728 1900 16.77 84% 94% 49% 65%
5. Rust #4
6.07 137,948 1749 18.11 51% 81% 78% 90%


C++ по-прежнему кажется недостижимым - чтобы посоревноваться за первое - нужен быстрый hashmap со специализацией по типам.

#rust #bench #old
1
coinbase-pro-rs

Запилил API на расте для работы с coinbase-pro: https://crates.io/crates/coinbase-pro-rs

поддерживает в том числе futures и websocket.
Ордербук пока положил отдельно - в связке он мне пока не нужен - но как только, так сразу его затащу внутрь.

#rust #old
ubuntu => alpine linux => ubuntu

Думал, о том, что ubuntu немного жирновата, посмотрел вокруг и наткнулся на alpine в образах scaleway, тут же поднял сервер. По-началу действительно немного меньше, но как только начинаешь всё ставить - то оно быстро вырастает до почти убунтувских размеров.

Собрал всё под неё, посмотрел на глаз сколько жрёт процессора - на глаз меньше прилично, смотрю дальше, ба! , да оказывается другой проц на scaleway поднялся. Поднял ещё одну убунту - замерял - alpine кажется немного больше жрёт cpu на той же задаче (я конечно понимаю точность сравнения top'ом :) ). Из приятного - всё кажется более простым и арчеподобным, пакеты аналогично - mongodb и rust из коробки довольно свежие. Пока не наткнулся на это: http://www.etalabs.net/compare_libcs.html , а потом ещё какие-то косяки с безопастностью и с производительностью тоже (причём свежие) нагуглились, ну и ну его решил, - опять убунту на scaleway поднял.

#linux #old
Rust, рефакторинг

Всё работало, вроде неплохо, пока не подключил extern crate pyo3. Естественно всё просто в Rust быть не может - не могу обернуть структуру с лайфтаймами макросами pyo3 => начался глобальный рефакториг по вычищению от лайфтаймов довольно центральной структуры => всё замазывается толстым слоем Rc/Arc/RefCell, настолько толстым, что хотется начать писать на Swift.

что-то типа такого:
self.borrow().control.borrow().stats.borrow_mut().counter += 1;


какой приятный код :(
При этом это по сути убивает всю lifetime концепцию - так как каждый borrow - это твой личный и ручной контроль поверх раста.

#rust #old
Rust - опять

Есть у меня pet-проект, который тянется уже больше года и, постепенно, подходит к финальному аккорду.

В какой-то момент было решено изменить способ хранения данных у клиента на простые файлы => понадобилась некая скачивался данных из базы для этого, всё работало почти отлично - данные качались чанк за чанком, прогрес-бар показывал сколько осталось. Кроме одного - данные не сжаты и их много - отдавать потоком - остаться без Content-Length - пользователь будет в неведении сколько осталось. Сжимать сразу много - пользователю очень долго ждать этого Content-Length.

Было решено просто - собирать по 1000, сжимать и отдавать - всё опять же работало, но что-то было странно - иногда некоторые чанки приходили битыми. Посмотрев на пакеты стало понятно - что пакеты нормальные, а вот способ их обработки у hyper такой - если chunk не влезает в буффер (8kb) - то выдавать chunk с тем что есть, а остальное - как будто это другой чанк. Насколько это корректное поведение не берусь судить, возможно и верное. Хотел было переехать на web-sockets, и тут, actix-web, казавшийся нормальным в качестве http показал, что структура для ws должна делаться так: Client<SinkWrite<Message, SplitSink<Framed<T, Codec>, Message>>> - просто же, и удобно - все кишки видны, а то мало ли нужно будет из client.0.0.0 извлечь codec.

Дальше надо описать актор, потом Handler, потом ещё StreamHandler, в который надо передать mut-контекст этого клиента, после чего из него дёргать руками методы типа ctx.0.write или обёртки. Но это не заработает со фьючовыми стримами, потому как при создании этого клиента - надо вызвать мутабельный add_stream на нём, в котором связать Stream и контекст - видимо иначе он не будет знать что надо дёргать этот handler, при этом, даже если совершить все эти действия - это только для обработки выходного потока из stream - как писать в него из другого стрима без своего impl Stream поверх - останется загадкой.

В общем, от такого юзать ws как-то расхотелось. Знающие люди говорили, что надо просто накрутить length_delimited кодек на чанки http и у меня будут нормальные фреймы данных - попробую в эту сторону. Кодеки работают через ASyncRead, а hyper/actix выдают Stream<Item=Vec<u8>> - хотя в общем-то понятно, что результат их работы в этом случае одинаков, и только в futures 0.3 сделали метод into_async_read() который позволяет одну абстракцию преобразовывать в другую => можно просто писать в codec - ура!, но нет, не ура, tokio 0.2 почему-то имеет свои ASyncRead, которые почему-то не совместимы с futures 0.3, а другие мне не подойдут. Есть некий клей - compat, но, бывалые люди, сказали что всё же _надёжнее разобрать байты руками_, чем пробраться через страницу какой-нибудь ошибки, которую покажет compat(). Без compat() ошибки и так по пол-страницы и не сильно проще читать.

Вернёмся к тому, что я никак не ожидал - что я могу застрять на несколько недель на простой скачивалке файла чанками, a раст, в очередной раз, как говорится, пробивает дно в производительности сроков написания чего-то отличного от примеров из папки examples (кстати и это часто не собирается). И у меня много таких случаев о которых можно было бы рассказать не меньше - но первый раз, когда решил написать.

#rust #old
Rust - достаточно

Rust практически вылетел из зоны моих интересов, жаль что довольно большой проект на нём застрял.

Ситуация такая: я уже почти увидел свет в конце тоннеля по проекту, но, на клиентской стороне использовать монгу не решился => нужно уметь читать данные как из монги так и из локальных файлов. В качестве такого решения напрашивается некий trait Storage, который реализует функцию создания итератора для структур Mongo и Local (из файла). Итераторы соответственно MongoIterator и LocalIterator которые реализуют просто Iterator. Проще, вроде как, и придумать трудно.

Собственно всё, что Local можно отбросить, чтобы просто сделать MongoIterator - надо в ней хранить и коллекцию и курсор, а это уже получается self-referential структура, после двух недель борьбы с Pin и unsafe (не смотря на то что пример вроде как есть тут: https://doc.rust-lang.org/std/pin/#example-self-referential-struct , но это не очень помогло), знающие люди подсказали поискать в крейтах, и действительно, нашёлся некий https://crates.io/crates/rental , который естественно полон макросов, а куда в расте без них, которые генерят это self-ref с лайфтаймами друг на друге и функции для их извлечения.

Кое-как всё пройдено, сам крейт проекта нормально собирается, теперь надо просто использовать это в бинарнике - и тут хоп - эта вот штука ещё используется, хотя вот тут уже конец блока, чуть ли не в main, с какой такой стати, почему - не ясно вообще, у меня уже начали опускаться руки, попытки понять лайфтаймов макросов, генерящих Pin, не сильно обрадовали и я пошёл искать тех самых знающих людей, которых мне помогут. Люди, как мне кажется, действительно знающие, нашлись и даже принялись за проблему, починили за час ... но нет, я просто случайно закомментил весь значимый код, раскомментил, и люди пришли к выводу, что разобраться в чём дело не удаётся. А это по сути просто интерфейс Storage - проще, мне кажется, и придумать нельзя.

На этом я решил, что больше свои руки, без необходимости, в это опускать не буду. Я благодарен расту за эксперимент-попытку поставить как основу всей системы move-семантику из плюсов, но это так просто не работает - ты бродишь по лабиринту, пытаясь удовлетворить borrow-чекер больше чем пишешь программу, повезло - вышел из лабиринта, не повезло - ты возвращаешься на много шагов назад и не факт, что выход в итоге есть. Иногда это интересная головоломка, но иногда, потратив на неё месяцы и опять оказавшись в тупике, ты понимаешь что это уже не смешно, работающего кода это не прибавило, а время потрачено без какой-то, ощутимой для проекта, пользы.

--- update ---
через три дня, мне постучались в телегу, и сказали, что не спали три ночи, но проблему исправили - ура.

#rust #old
👍1
Попытался переехать на linux, и откатился назад

После прочтения https://www.phoronix.com/scan.php?page=article&item=icelake-clear-windows&num=1, поставил разные линуксы на второй ssd:
из приятного - действительно время сборки с ходу в два раза меньше.
CPU-бенчмарки тоже показали что тут собирается как-то лучше: https://github.com/inv2004/rust_vectors/blob/master/win_lin.md

Однако, того, что при установке пакетов зависает мыша, dd if=1.iso of=/dev/sdb1 вообще даже на kill -9 не реагирует, гном упал один раз, хотя у меня просто lenovo без всяких nvidia. в общем, чисто на уровне ощущений убунта оставила очень неприятные впечатления, зато приятные впечатления оставили elementary-OS и dwm+antiX (хотя наверное стоило бы MX-Linux).

из печального - chromium даже на странице с простыми баннерами бывает поджирает под 30% CPU, надо постоянно что ли на top смотреть чтобы просто браузером пользоваться? скроллинг заметно хуже, youtube вообще не говорю - под 100% CPU улетают на простых даже видео - т.е. пользоваться всем этим не очень приятно, я-то думал такой проблемы нет, а за 20 лет до сих пор в линуксе нету браузера по сути нормального.

В чате меня спросили, сколько бы я лично пожертвовал на развития десктопа linux'а, я ответил какую-то сумму, но после убунты я решил, что фиг бы я за это стал вообще платить, а лучше бы отдал, чтобы разобрать что же такое притормаживает у меня в винде, и действительно вспомнил, что IDEA что-то там делает с антивирусом - попробовал, и это разогнало сборку cargo, хоть и не до линуксовых значний, но значительно ближе.

Осталось разобраться с этом: https://github.com/inv2004/rust_vectors/blob/master/win_lin.md
Но там код не такой уж сложный - попробую сравнить C++ - вариант.

#linux #old
inv2004 Dev Blog
Попытался переехать на linux, и откатился назад После прочтения https://www.phoronix.com/scan.php?page=article&item=icelake-clear-windows&num=1, поставил разные линуксы на второй ssd: из приятного - действительно время сборки с ходу в два раза меньше. CPU…
— update —
провёл ~ неделю на elemenetary - она даже понравилась, впрочем отличия от разных ubuntu не очень значительные. Открытием стало, что в аpt всё настолько протухшее, что много чего ставить из snap или flatpack, даже neovim.

но окончательную точку поставило, что на батарее ноут на одной батарее живёт около 4часов, когда под win более 5 1/2, и то, под lin я старался ничего лишнего не запускать.
Duck-typing problems of kdb

t:([] k:`a`b`a; v:1 2 3)
select k, v, kk:?[1=v mod 2; {`$a,a:string x} each k;k] from t
k k1 v
-------
aa a 1
b b 2
aa a 3

select k, v, kk:?[1=v mod 2; {`$a,a:string x} each k;k] from 0#t
'type
[0] select k, v, kk:?[1=v mod 2; {`$a,a:string x} each k;k] from 0#t

The root cause of the problem is quite simple:
type {x} each `a`b
11h
type {x} each `$()
0h // brakes everything, because ?[] check type of the second argument

#kdb #en #old
This media is not supported in your browser
VIEW IN TELEGRAM
Tiling WM for Windows-10

Одна из причин, по которой меня иногда тянуло обратно на linux была dwm - https://dwm.suckless.org/

Помню ещё давно искал что-то подобное на Win-10, но нормально рабочих решений не было, да и комбинации win+стрелки работали более-менее. Однако, неожиданно на github нашлось хорошее решение: https://github.com/workspacer/workspacer

По виду видно, что это копировали именно с dwm, а не с чего-то другого. Пользуюсь - удобно. Есть конфиги с которыми пока не разбирался.

TaskBar на экране есть, хотя на скриншотах с их сайта его не видно.

#windows
Настройки клавиатуры для windows

Несколько лет были проблемы с Ctrl - мне казалось, что нажимать её крайне неудобно, после чего обнаружил, что я не один такой, и многие переносят её на CapsLock. Помогло, но всё же не полностью - Ctrl+Z например, и, около года назад, обнаружив https://github.com/microsoft/PowerToys, я скопировал Mac решение - а именно, перенести Ctrl на кнопку слева от пробела, а Alt на соседнюю - и, кажется, что это лучший вариант на текущий момент.

Аналогично пришлось поменять системные Alt+Tab <=> Ctrl+Tab

#windows
Channel name was changed to «inv2004 Dev Blog»
Для kdb-разработчиков может быть интересно - ThePlatform:

ThePlatform - они в своё время не договорились с kx по цене на 100+ лицензий (оно и не удивительно если вы - не банк) и за пару лет написали О - это копия kdb, включая очень похожий синтаксис, но с некоторыми улучшениями. Я бы про них не вспомнил, но они выложили на github код всей системы.

Вот анонс: https://www.reddit.com/r/apljk/comments/jr0z1d/new_vibe_man_in_town_preliminary_announcement_of/

Основные отличия:
- Supports streams
- Queries can be "lazy"
- Implements join calculus

https://theplatform.technology/
https://github.com/o83/n2o

#kdb
100M Taxi Rides benchmark. 1/3

Сначала хотел написать о популярном и не менее странном бенчмарке баз данных https://tech.marksblogg.com/benchmarks.html, но, вдаваясь в детали, стало понятно, что проще и надёжнее перетестировать самому.

Из всего этого зоопарка интересно смотреть только на топ, исключая GPU-решения. В итоге, там остаются только Clickhouse, Kdb и Hydrolix. Последнее - не знаю что такое. Теперь о самом тесте: загружается достаточно большой объём данных, после чего делается бенчмарк четырьмя запросами, но, почему-то всего по трём колонкам. Не очень понятно зачем загружать 53 огроменные колонки чтобы использовать всего 3, если только посмотреть на время загрузки. Но, возможно, для невекторных баз, это будет иметь влияние на тест.

В топе, по не-GPU, там kdb, но, при этом, тест происходит не на одинаковом железе, и, kdb, запускающийся на Xeon Phi кластере с MCDRAM, соревнуется с Clickhouse на i5 2013го года с 16Gb DDR3.

Отдельный вопрос формирования самих данных, но об этом в следующих частях.

Итого, я скачал k9 (shakti), kdb, Clickhouse, немного сократил (относительно исходного теста, и расширил относительно этого же теста от shakti) и поправил исходные данные, так как запускаю локально. Итого, данных всего ~1.4Gb, что должно быть не слишком много чтобы не дёргать ssd - он же у меня с али. Помимо того, в исходном kdb-тесте допущены ошибки в запросах https://www.reddit.com/r/apljk/comments/s25ch1/error_in_billion_taxi_rides_on_kdbq_benchmark/, так и в k9/kdb делают некоторую предварительную подготовку данных специально для ускорения последующих запросов, а, в clickhouse, в q4 делают не очень большую, но всё же сортировку данных, чего не делают в kdb. Также не очень понятно что происходит в k9 с параллелизацией, но зато k9 умеет делать партиционирование таблицы прямо в памяти, а kdb только на диске.

Результат на скриншоте ниже и тут: https://github.com/inv2004/100m_taxi_bench

Итог: Так как доступная k9 работает в однопоточном режиме, то можно сделать вывод, что k9 сильно оптимизирован относительно kdb, особенно в плане применения SIMD, но kdb выезжает за счёт -s 4. k9 пока не умеет группировку по составному ключу если он не партиционный. На простых запросах Clickhouse проигрывает за счёт sparse индекса, но на более сложных запросах за счёт него же и выигрывает.

В следующих частях хочется написать про срезание углов (лучше сказать - применение разные эвристик) для лучшего результата бенчмарка.

#k9 #kdb #clickhouse #bench
👍1
100M Taxi Rides benchmark. 2/3

В прошлой записи я писал о формировании самих данных и почему это может быть важно при бенчмарке. Проведём небольшой эксперимент с k9 - пересоздадим таблицу так что колонка ct будет содержать цифры не 0..3, а, например, 10..13, хотя тип остался тот же. Замеряем ... вот тебе на, время запроса скакнуло с ~30ms до 90ms+. Казалось бы, можно ожидать что при одинаковых типах будет одинаковая производительность, но, оказывается, это не всегда так. Отсюда можно предположить, что k9 накладывает битовую маску чтобы, например, числа младших бит, которые используются в тесте - считались быстрее. Данный запрос select count by ct from t - является вариантом подсчёта гистограммы, но оптимизируется только если результат содержит довольно небольшое количество масок, чтобы процессор мог обрабатывать параллельными бранчами конвейера, при непопадании в данную маску - происходит откат к "дедовскому" способу подсчёта. Нативная реализация этого на SIMD/AVX2 интрисиках показывает 11ms: https://gist.github.com/inv2004/099321a52b54a9410b54446b3ca9a9c2

Но, раз в ход пошли такие оптимизации, то не стоит останавливаться только на накладывании битовых масок, как я писал в прошлой части, для Clickhouse почему-то проводят сортировку по времени => ряд/колонка ct будет часто меняться. Однако для kdb такой сортировки не делают => ct будет иметь очень много повторений и меняться очень редко, что-то типа 1 x95000 , 2 x95000 , … и так далее. В k9 данный ряд будет выглядеть так: ct:`g ,/95000#'!4. В k9 ничего не поменялось: 30ms или 90ms, в зависимости от старшего бита числа. В собственной реализации можно легко учесть этот момент: делается маска байт из первого байта батча размером 256 (avx2) байт и накладывается на весь ymm регистр - https://gist.github.com/inv2004/503e73aa5c14ff8ba9534c7e64ad04c6 => итоговое время сократится до 8ms, причём в не зависимости от того какого диапазона числа находятся в векторе.

Итог: Тяжело судить хорошо ли применять такие эвристики, как минимум не упомянув о них в документации, потому как, наверное, мало приятного, что производительность может неожиданно взорваться просто на основании того, что в систему начали приходить немного другие числа (без изменения типа), с другой стороны это открывает большой простор для оптимизации под конкретные тесты. В языках с типизацией, вероятно, правильнее вводить новые типы под данные ограничения оптимизации, но k9/kdb не умеют в типы. Если типы не доступны, тогда, хорошо сработает что-то типа hint'ов или дополнительных атрибутов в kdb - которые позволят делать подобный тюнинг в зависимости от данных.

#k9 #kdb #bench
👍3