Минутка просвещения

Читать в телеге. Когда-то там были посты не только от меня.

Сколько мониторов нужно для счастья

За время своей работы встречал разнообразные мнения по данному вопросу:

  1. Нужно 4 монитора! На одном реверсить, на другом даташит открыт, на третьем среда разработки, на четвертом разводка платы… Больше мониторов богу мониторов!
  2. Два монитора норм, на одном кодишь, на другом открыт тикет/чат/порнуха/унылый созвон. Или запускаешь созвон на одном экране, а другой шаришь.
  3. Нужен только один монитор (но большой), потому что если много мониторов — теряешь фокус на одной задаче.

Когда-то мне казалось, что надо 3 монитора, но сейчас я склоняюсь к умеренному варианту №2, причем это экран ноута + монитор. На одном большом экране даже со всякими приемами разделения окон может быть не очень удобно переключаться между контекстами (а на маке разделение экрана само по себе конченое). А двух экранов достаточно, чтобы выполнять переключение поворотом головы.

Вообще, переключение контекста — довольно дорогая операция. И если их нужно одновременно больше двух, то, возможно, вы что-то делаете не так? Может надо сделать какую-то предварительную обработку “вспомогательных” контекстов, чтобы они работали как один?

А что говорит по этому поводу наука? Нашел мета-исследование на тему (по DOI можно найти на сцихабе полный текст). Если смотреть на опросы пользователей, то там все понятно: минимум 17”, большинству подавай два монитора. Однако кто говорил, что ему надо четыре, не использовал их по полной. Если смотреть на более объективные измерения, то в одном исследовании измеренная продуктивность выше с двумя мониторами, чем с одним, но при этом третий монитор существенно на нее не влияет. В другом исследований не обнаружили разницу между одним и двумя мониторами 17” или 22” (зависит от задачи, в общем). При этом есть умеренные свидетельства уменьшения числа кликов/переключений окон при увеличении размера и количества мониторов.

СсылкаКомментировать

Работа с файловой системой

Подобно тому, как стоит избегать писать свои реализации всякой криптографии и делать что-то со временем, есть еще одна область в которую лучше не лезть: работа с символическими ссылками и файловой системой вообще.

Правильно реализовать работу с символическими ссылками — довольно нетривиальная задача. Хорошую презентацию по этому поводу можно посмотреть тут. Если вкратце, то ссылки могут меняться между системными вызовами, что открывает массу возможностей для повышения привилегий из-за того, что их проверка и действие над ссылкой происходит неатомарно. Даже в системных вызовах Linux не смогли поправить это с первого и даже со второго раза.

А еще символические ссылки ломают иерархию (и связанные с ней проверки) — например, путь с .. уже нельзя нормализовать без запроса к файловой системе. А еще ссылка может ссылаться на другую ссылку, и эти случаи в два раза веселее обрабатывать.

В добавок к этому, я недавно нашел интересное поведение в java. Если создать папку, содержащую юникодные символы, то ее имя по-разному будет нормализовано в зависимости от того, был ли использован метод Files.createDirectory или File.mkdir. Это приводит к тому, что оригинальный путь не совпадает с “реальным” путем к файлу, который только что был создан по этому пути:

package test;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Function;


public class App {

    @FunctionalInterface
    public interface Action {
        void apply(Path path) throws IOException;
    }

    public static void main(String[] args) throws IOException {
        check("createDirectory", Files::createDirectory);
        check("mkdir", x -> x.toFile().mkdir());
    }

    private static void check(String hint, Action createDir) throws IOException {
        String name = "teŝt files";
        Path root = Path.of(name).toAbsolutePath();
        Path file = root.resolve("file1");

        Files.deleteIfExists(file);
        Files.deleteIfExists(root);
        createDir.apply(root);
        Files.createFile(file);

        Path alternative = file.toRealPath();

        System.out.printf("%s - Equals: %s, isSameFile: %s\n", hint, file.equals(alternative), Files.isSameFile(file, alternative));
    }
}

Вывод:

createDirectory - Equals: true, isSameFile: true
mkdir - Equals: false, isSameFile: true
СсылкаКомментировать

Запись команд терминала

Чтобы записать демонстрацию какой-нибудь последовательности команд, можно воспользоваться Asciinema. Работает примерно так:

Можно конвертировать результат в гифку или использовать js-проигрыватель.

На удивление, эта штука без проблем сработала, когда я ее запустил внутри самой себя. В целом прикольно, но мне текстовый формат нравится больше — оттуда, как минимум, проще копировать. Еще один минус — вся тупка при записи записывается, т.е. нет нормализации: задержки и ошибки при печати команд тоже сохраняются. С другой стороны, за счет этого запись более “живая”.

СсылкаКомментировать

Аннотации массивов в java

Психолог: заковыристые типы в Java не существуют, они не могут причинить тебе вреда.

Тем временем java: @Nullable List<? extends @Nullable Object> @NotNull [] @Nullable [] someVar.

Вьетнамские флешбеки от сишных указателей.

Если серьезно, то массивы всегда были “особенными”: вроде и не примитивы, но и не классы. Еще и куча исключений, настолько, что это в Kotlin просочилось.

СсылкаКомментировать

Двойные и тройные слеши в пути

Нашел прекрасное. В POSIX ////some/path, ///some/path и /some/path — это одно и то же, но при этом не то же самое что //some/path. 3 и более слешей спереди превращаются в одинарный слеш, а вот обработка 2 слэшей может зависеть от системы/реализации. И нет, это не из-за Microsoft.

В большинстве случаев, например в Cygwin, //host/path интерпретируется как сетевой ресурс. Но не сетью единой: например, в Bazel // используются для адресации таргетов.

СсылкаКомментировать

Локальный запуск LLM

TLDR: Если вам нужен простой локальный GPT, попробуйте Ollama. И разумеется, если у вас не зверь-машина, то все модели будут тупее ChatGPT.

Вообще я поразился, как много уже доступно предобученных моделей для скачивания — тысячи их! Все я пробовать, разумеется, не стал, выборочно посмотрел готовое с интерфейсами.

  1. Сначала я попробовал LM Studio. У него захламленный и запутанный интерфейс. Кроме того, это закрытое ПО, и нет поддержки для Linux. Однако там без проблем можно скачивать LLM-модели и легко переключаться между ними. Для перебора LLM — сойдет. Я попробовал модель “codellama instruct 7B q4” — пишет плохонький Python-код и предлагает плохие советы по Gradle.
  2. Затем я попробовал GPT4All. Довольно минималистичный интерфейс, хоть и лагает иногда, есть встроенная функция дообучения на локальных документах. Однако функция скачивания моделей сломана. Я использовал “GPT4All Falcon q4”. Он выдаёт абсолютный бред в ответ на вопросы по Gradle даже когда дообучен на документации, иронично указывая точные страницы из PDF-файла исходных данных.
  3. После этого я попробовал Ollama. Нет GUI, но с ней меньше всего гемора, ИМХО. Немного неудобен многострочный ввод (решается тройными кавычками). Иногда модель ломается и выдаёт несвязанные данные из-за проблем с парсингом. Увы, я не нашёл простого способа дообучить модель на локальных файлах. Я использовал модель “llama2-uncensored” с ним. Да, вы прочитали правильно, ее можно спросить, как роскомнадзорнуться, например (Я покекал с “Ultimately, the best way to RKN is one that you feel confident will ensure your safety and comfort during the process, with minimal risk of unforeseen consequences or complications”). Думаю, это приемлемая замена для GPT — все равно тупая, но зато без тормозов. Для вопросов по Gradle модель давала плохие ответы, но по крайней мере они содержали полезные части, а не только дичь.
  4. Наконец, я ознакомился с PrivateGPT. Установка не очень удобная, но и ничего сложного. Тоже нет GUI, но можно дообучать. Я использовал рекомендованную модель “gpt4all-j-v1.3-groovy”, дообученную на документации Gradle. Полный бред в ответ на вопросы по Gradle, еще и цитирует несуществующий текст из руководства.
  5. Есть еще LLamaGPT, который будет удобнее запустить в докере, но до него руки не дошли.

Теоретически можно загрузить модель Ollama в интерфейс GPT4All и дообучить, но я заленился это делать.

СсылкаКомментировать

Уведомления от сервера клиенту

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

До этого N работ назад использовали Crossbar.io, но когда я писал этот пост, обнаружил, что он уже при смерти.

А еще когда я смотрел альтернативы Centrifugo, то наткнулся на Server-sent Events (aka SSE, aka EventSource). Это стандарт для веба, который позволяет в одностороннем порядке слать уведомления. Рандомную демку можно посмотреть тут, работает даже с curl из коробки. В общем, жду светлые времена, когда в web-стандарте начнут поддерживать докер-контейнеры, чтобы там кубер свой запустить…

СсылкаКомментировать

Переводчик Curl

Прикольный конвертер, который без смс и ChatGPT позволяет перевести Curl-запрос в код на одном из популярных языков. На мой взгляд, весьма полезно, так как в Curl любая помойка умеет экспортировать, а переписывать это руками лениво.

Работает не 100% идеально, но немного времени сэкономит. Мне пригодилось, чтобы пример из GitHub API перевести в питонячий код.

СсылкаКомментировать

Вложенные куски кода в Markdown

Когда в GitHub пользовался функцией предложения изменений (классная штука, кстати) для файла Markdown, понадобилось вставить блок кода.

Выглядело это изначально так:

```suggestion
some words
```kotlin
some code
```
```

Парсер комментария от такого, разумеется, поломался: не понял, что первые ``` это конец блока кода, а не предложенного изменения. Оказалось, что у некоторых реализаций Markdown есть фича, что можно вместо трех обратных апострофов можно использовать 4 и больше. И тогда можно сделать так:

````suggestion
some words
```kotlin
some code
```
````

и все вложенные уровни распарсятся корректно. Разумеется, если посмотреть в исходники этого поста, то там тоже будет использоват этот же трюк.

СсылкаКомментировать