Если я добавил новую настройку с ограничением, по семверу это бампит минорную версию, или патч?
Пришлось переименовать имя модуля (как же в го всё с этим сложно). Аж офигел, насколько долго тестики проходят - всё же хорошо, что
go test кэширует результаты👍1
Больше, чем отсутствие актуальных версий в репозиториях, я ненавижу версионирование и работу с ним тулчейна го.
Что делать
Больше, чем отсутствие актуальных версий в репозиториях, я ненавижу версионирование и работу с ним тулчейна го.
Это просто ужас. Почему-то с
v2.3.1-alpha оно ну никак, а с v 2.3.1-beta зафурыкало. Надеюсь, больше никогда не придется работать с этой фигнёйДумаю, все знают, что можно после запуска тестов получить профайл покрытия кода. Но я лично только сейчас узнал, что из коробки есть
Если что,
go tool cover. Он в том числе может открывать страничку, где подсвечен код, который покрыт тестами, а который - не покрытЕсли что,
go test -coverprofile=c.out ./... && go tool cover -html=c.out👍2
Кстати, сегодня индиге исполнился ровно год - первый коммит в репозиторий был совершён 3 марта 2022 года
Итак, а что же поменялось?
- Я научился в оптимизации, даже вынес для себя некоторые основные приёмы. Кратко - сначала упрощай алгоритм до невозможности, потом приступай к низкоуровенщине
- Решил достаточно много дизайнерских проблем. В данном случае - "а как удобнее сделать ту или иную фичу". Не то, чтобы я действительно понимал, почему то или иное решение удобнее в проде, но внешне выглядит в принципе симпатично
- Подтвердил для себя теорию, что любая задача кажется тривиальной, если известно решение. Довольно забавно с высока вспоминать времена, когда я ещё не мог на питоне асинхронный фреймворк написать :)
- Научился чуть лучше работать с крупными проектами. На данный момент, фреймворк насчитывает почти 100 файлов и 8.2к строк кода (примерно треть - тесты), что лично для меня - много, поскольку раньше обычно дальше 2-2.5к строк кода проекты не заходили. Фан факт: последний раз, когда мне приходилось рефакторить проект практически с нуля, был на отметке в 3.5к строк кода. Тогда архитектура и сам код обвалились под собственным весом, преодолев критическую массу, когда абсолютно вся работа сводилась к починке багов. Когда починил - образовались новые. Заштопывать заплатки, рвущиеся сразу же после наложения, в общем
- Научился в github actions. Оказалось, что это даже не такая уж и сложная штука - хоть и было сложновато, пока я не додумался почитать документацию :)
- Стал больше уделять внимания описанию коммитов. Около двух лет метался из крайностей в крайности, пока наконец не пришёл к некоторому универсальному стилю. TL;DR - прислушался к названиям полей для ввода в GUI-клиентах гита
- Даже логотип сверстал! Правда, документации как не было, так и нет :) Однако я уже приступил к гайду (который я ни в коем случае не копирую из фибера). Надеюсь, скоро наконец сбудется, и я смогу полноценно гитбук на кастомный домен github pages нацепить.
Всем чаю!
- Я научился в оптимизации, даже вынес для себя некоторые основные приёмы. Кратко - сначала упрощай алгоритм до невозможности, потом приступай к низкоуровенщине
- Решил достаточно много дизайнерских проблем. В данном случае - "а как удобнее сделать ту или иную фичу". Не то, чтобы я действительно понимал, почему то или иное решение удобнее в проде, но внешне выглядит в принципе симпатично
- Подтвердил для себя теорию, что любая задача кажется тривиальной, если известно решение. Довольно забавно с высока вспоминать времена, когда я ещё не мог на питоне асинхронный фреймворк написать :)
- Научился чуть лучше работать с крупными проектами. На данный момент, фреймворк насчитывает почти 100 файлов и 8.2к строк кода (примерно треть - тесты), что лично для меня - много, поскольку раньше обычно дальше 2-2.5к строк кода проекты не заходили. Фан факт: последний раз, когда мне приходилось рефакторить проект практически с нуля, был на отметке в 3.5к строк кода. Тогда архитектура и сам код обвалились под собственным весом, преодолев критическую массу, когда абсолютно вся работа сводилась к починке багов. Когда починил - образовались новые. Заштопывать заплатки, рвущиеся сразу же после наложения, в общем
- Научился в github actions. Оказалось, что это даже не такая уж и сложная штука - хоть и было сложновато, пока я не додумался почитать документацию :)
- Стал больше уделять внимания описанию коммитов. Около двух лет метался из крайностей в крайности, пока наконец не пришёл к некоторому универсальному стилю. TL;DR - прислушался к названиям полей для ввода в GUI-клиентах гита
- Даже логотип сверстал! Правда, документации как не было, так и нет :) Однако я уже приступил к гайду (который я ни в коем случае не копирую из фибера). Надеюсь, скоро наконец сбудется, и я смогу полноценно гитбук на кастомный домен github pages нацепить.
Всем чаю!
❤2
А, ну и самым знаковым моментом за всё существование проекта - я по праву считаю переход с модели разделения пользовательского и серверного пространства, на одно общее пространство. Я, кажется, об этом ещё не писал, но вот краткое описание оного:
Разделение пользовательского и серверного пространств подразумевает две горутины. В первой происходит вся внутренняя кухня, начиная с чтения из сокета, заканчивая http-специфичными штуками. Во второй - ждём сигнала из оповестительного канала. При получении, вызываем обработчик, при завершении - извещаем об этом ядро. И на этом строится вся синхронизация, от которой зависит половина кодовой базы. Это, помимо постоянных дедлоков при изменении логики работы данного механизма (причина нескольких переписываний чуть ли не с нуля), было очень удобным для имплементации потоковой обработки тела. Но была одна проблема...
...и заключалась эта проблема в чрезмерном количестве синхронизаций, основанных на межканальном взаимодействии. А происходили они действительно часто, особенно при работе с телом, и избежать их нельзя было никак (даже если с телом никто не взаимодействует). Это было медленно. Это создавало слишком частые переключения. Это приводило к повышению задержек. Это, в конце концов, приводило к нестабильным результатам бенчмарков - погрешность была больно уж велика.
Новая же модель подразумевает, что http-парсер будет парсить до тела. Само же тело он не трогает. Как только спарсились заголовки - вызывается обработчик, и уже внутри обработчика при потребности происходит чтение из сокета, и собственно обработка тела. Сам сокет был обёрнут в некий объект, который умел хранить "ненужные" данные, которые будут возвращены при следующем чтении - чтобы вычитав заголовки с кусочком тела, всё не сломалось к чертям. Это и позволило безболезненно разделить оба процесса парсинга друг от друга.
В результате, бенчмаркать стало немного труднее, однако это не есть проблема. Сами же бенчмарки стали более обширными, покрывая также tcp-сервер (однако не совсем точным образом; палки в колёса вставляют таймауты на чтение). Не смотря на это, прирост составил около трёхкратную разницу в случае с GET-запросом на 10 заголовков (3000ns -> 860ns).
Собственно, суть данного поста в донесении следующих мыслей:
- горутины сами по себе не дорогие; дорого их синхронизировать
- здесь сработал принцип упрощения алгоритма: несмотря на когнитивное усложнение архитектуры, отныне из алгоритма исключена целая куча синхронизаций. Это привело не только к ускорению кода, но и к тому, что работать с ним стало проще - меньше потенциальных мест, изменения в которых могут привести к отстреленной ноге
Разделение пользовательского и серверного пространств подразумевает две горутины. В первой происходит вся внутренняя кухня, начиная с чтения из сокета, заканчивая http-специфичными штуками. Во второй - ждём сигнала из оповестительного канала. При получении, вызываем обработчик, при завершении - извещаем об этом ядро. И на этом строится вся синхронизация, от которой зависит половина кодовой базы. Это, помимо постоянных дедлоков при изменении логики работы данного механизма (причина нескольких переписываний чуть ли не с нуля), было очень удобным для имплементации потоковой обработки тела. Но была одна проблема...
...и заключалась эта проблема в чрезмерном количестве синхронизаций, основанных на межканальном взаимодействии. А происходили они действительно часто, особенно при работе с телом, и избежать их нельзя было никак (даже если с телом никто не взаимодействует). Это было медленно. Это создавало слишком частые переключения. Это приводило к повышению задержек. Это, в конце концов, приводило к нестабильным результатам бенчмарков - погрешность была больно уж велика.
Новая же модель подразумевает, что http-парсер будет парсить до тела. Само же тело он не трогает. Как только спарсились заголовки - вызывается обработчик, и уже внутри обработчика при потребности происходит чтение из сокета, и собственно обработка тела. Сам сокет был обёрнут в некий объект, который умел хранить "ненужные" данные, которые будут возвращены при следующем чтении - чтобы вычитав заголовки с кусочком тела, всё не сломалось к чертям. Это и позволило безболезненно разделить оба процесса парсинга друг от друга.
В результате, бенчмаркать стало немного труднее, однако это не есть проблема. Сами же бенчмарки стали более обширными, покрывая также tcp-сервер (однако не совсем точным образом; палки в колёса вставляют таймауты на чтение). Не смотря на это, прирост составил около трёхкратную разницу в случае с GET-запросом на 10 заголовков (3000ns -> 860ns).
Собственно, суть данного поста в донесении следующих мыслей:
- горутины сами по себе не дорогие; дорого их синхронизировать
- здесь сработал принцип упрощения алгоритма: несмотря на когнитивное усложнение архитектуры, отныне из алгоритма исключена целая куча синхронизаций. Это привело не только к ускорению кода, но и к тому, что работать с ним стало проще - меньше потенциальных мест, изменения в которых могут привести к отстреленной ноге
Что делать
Загадка от Жака Фреско: угадайте, где здесь происходит аллокация на 24 байта. На ответ даётся 30 секунд
Ответ на загадку: аллокацию вызывает передача метода
Сейчас буду пытаться разузнать причину этого. Даже интересно стало, что же оно там такого аллоцирует на 24 байта🤨
client.WriteСейчас буду пытаться разузнать причину этого. Даже интересно стало, что же оно там такого аллоцирует на 24 байта🤨
В общем, я так и не понял, почему так, но штуку с горем пополам пофиксил. Главное - не забыл оставить гневное послание core-мейнтейнерам в комментарии
Поскольку заголовки всё ещё являются узким горлышком, я не прекращаю попыток придумать способы их соптимизировать. На повестке дня два способа:
- Статическая структура с well-known заголовками, чтобы лишний раз их не искать в общем хранилище
- Заранее объявлять списки нужных заголовков
С первым способом особо проблем нет, это имплементируется довольно банальным способом. Больше нас интересует второй пункт. Его идея состоит в том, чтобы если заранее известны необходимые обработчику заголовки, то их можно было сложить в одну структуру, и их запись/извлечение имели константную асимптотику. Иными словами - хэшмапа, но работающая на заранее известном множестве ключей. Более того, такая штука также может использоваться для хранения динамических сегментов путей (во встроенном роутере), что также повышает производительность.
Пока что вся проблема сводится к достаточно умной хэш-функции, которая могла бы выдавать желательно непрерывные (либо с небольшим интервалом) значения для строк. Надеюсь, я смогу что-то такое найти.
Эх, сколько же проблем решает сильный компайл-тайм
- Статическая структура с well-known заголовками, чтобы лишний раз их не искать в общем хранилище
- Заранее объявлять списки нужных заголовков
С первым способом особо проблем нет, это имплементируется довольно банальным способом. Больше нас интересует второй пункт. Его идея состоит в том, чтобы если заранее известны необходимые обработчику заголовки, то их можно было сложить в одну структуру, и их запись/извлечение имели константную асимптотику. Иными словами - хэшмапа, но работающая на заранее известном множестве ключей. Более того, такая штука также может использоваться для хранения динамических сегментов путей (во встроенном роутере), что также повышает производительность.
Пока что вся проблема сводится к достаточно умной хэш-функции, которая могла бы выдавать желательно непрерывные (либо с небольшим интервалом) значения для строк. Надеюсь, я смогу что-то такое найти.
Эх, сколько же проблем решает сильный компайл-тайм