Как стать автором
Обновить
39
0
Дмитрий @dmitryikh

Software Developer

Отправить сообщение
Закралась мысль, что у Сбера стратегия информатизации такая.
Есть у Сбера деньги и нет ИТ технологий. Делаешь СП с ИТ компанией. Разводишься. Получаешь ИТ технологии в актив.
Для меня ценность vi/vim'а — в стиле навигации и набора текста (hjkl, модальный ввод и пр. что есть из коробки). Сам vim использовать в качестве IDE — увольте. Во-первых, terminal обладает далеко не богатыми UI возможностями. Во-вторых, настраивать плагины и оттачивать vim.config — то еще удовольствие (и не продуктивно).

Я всегдаиспользую «vim mode» в Visual Code, Visual Studio, CLion,… Но сам vim запускаю очень редко, когда надо глянуть файл не выходя из терминала.
По привычке начал искать vim mode. Не нашел.
А так — выглядит и работает хорошо! Жалко, что применение скорее всего ограничится только онлайн кодинг интервью.
Сам был участником подобных собеседований в FAANG. Поэтому было интересно узнать, почему собеседования проходят именно таким образом: 3 кодинг сессий, 1 дизайн системы, 1 поведенческая.

Для себя сделал такой вывод:
1. Большой поток кандидатов, сложно объективно и индивидуально подходить к каждому.
2. Решая алгоритмические задачи, кандидат показывает свои когнитивные способности. Способность понимать подсказки интервьюера, способность находить и обрабатывать крайние случаи. Отбираются кандидаты с хорошей соображалкой, а уж по скиллам место внутри многотысячной корпорации найдется.
3. Подготовленный кандидат (over 50-100 решенных задач на leetcode, например) также показывает свою мотивацию получить работу в компании.

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

Как новый лидер узнает о незавершенных транзакциях? Не может ли возникнуть ситуации, когда в слейве будет «потеряшка» — старая транзакция без подтверждения или отмены. Классическая проблема двухфазного коммита.

Полиморфные аллокаторы интересны для оптимизаций выделения памяти «на куче». Например, через них можно сделать аллокации в арене (как protobuf arena) и очистить всю память разом, освободив арену.

Один момент, который смущает — это хранение указателя на аллокатор в каждом объекте. Для контейнеров такой overhead может быть незаметен, а вот для строк — это плюс одна треть к размеру объекта. Вот думаю, можно ли провести оптимизация: хранить указатель на аллокатор в статической области памяти (thread_local). И перед работой с контейнером назначать этот указатель на необходимый аллокатор. Есть ли у кого подобный опыт?
Да, вы правильно поняли. На самом деле нужно убрать предварительную проверку:
if (initialized) // early return to avoid touching mutex every call
	return;
Я описал как в железа на x86-x64. На других архитектурах механизмы другие, но будут соблюдены гарантии модели памяти C++. Компилятор не в праве компилировать корректный с точки зрения стандарта языка код в некорректный машинный код.

Поэтому — да, так всегда by design языка.

Кстати, насчет счетчика ссылок. Для инкремента можно использовать relaxed, а для декремента нужен acquire/release, т.к. декремент счетчика ссылок до 0 должен быть синхронизирован для однократного вызова деструктора объекта и деаллокации памяти.
Может ли случиться, что результат будет, как будто инкремент случился лишь единожды?

Нет, инкремент случится всегда дважды. Об этом пример 1, как раз.
godbolt.org/z/jx85P9

Код:
counter.fetch_add(1, std::memory_order_relaxed);

Транслируется в команду на x86-64:
lock add        QWORD PTR counter[rip], 1

На уровне ЦПУ это работает так: выполнить оператор add, захватив эксклюзивный доступ к кэш линии. Это соответсвует состоянию Exclusive в протоколе синхронизации кешей MESI ( en.wikipedia.org/wiki/MESI_protocol ). Состояние exclusive означает, что в линии кэша ядра лежит актуальное значение (соотвествующее main memory) и в других ядрах кэш линии инвалидируется (состояние invalid).

Когда два ядра захотят выполнить fetch_add(1, std::memory_order_relaxed), то им придется по-очереди захватить exclusive лок на кэш линию и каждое ядро (поток) прибавит свою единичку правильно.

Ядре (архитектура x86-64), выполняющем print_metrics(), кэш линия с counter будет в состоянии invalid, ядро запросит чтение из этой кэш линии, и она перейдет в состояние shared на всех ядрах. В этот момент в кэш линии уже будет актуальное значение, совпадающее с количеством fetch_add(1), которые успели выполнится в других потоках.
Спасибо за ваши развернутые замечания!

По 1. Полностью с вами согласен. Я подправлю текст статьи.

По 2. Все таки не соглашусь с вами. Атомарные операции (инструкции процессора) — более фундаментальное понятие, чем мьютекс в операционной системе. Реализация мьютексов в OS реализованы на атомиках (по крайней мере в linux).

Вот кусок из mutex.c из исходных кодов линукс:
/*
 * Optimistic trylock that only works in the uncontended case. Make sure to
 * follow with a __mutex_trylock() before failing.
 */
static __always_inline bool __mutex_trylock_fast(struct mutex *lock)
{
	unsigned long curr = (unsigned long)current;
	unsigned long zero = 0UL;

	if (atomic_long_try_cmpxchg_acquire(&lock->owner, &zero, curr))
		return true;

	return false;
}

static __always_inline bool __mutex_unlock_fast(struct mutex *lock)
{
	unsigned long curr = (unsigned long)current;

	if (atomic_long_cmpxchg_release(&lock->owner, curr, 0UL) == curr)
		return true;

	return false;
}


Вы правы, что мьютекс в OS более «умный», чем спин лок и взаимодействует с планировщиком потоков, но это не убирает с него обязанностей атомарно «включить» или «выключить» блокировку, что реализована на атомиках, как показывает исходный код выше.

3. Про compare_exchange_weak vs strong. Я с вами согласен. Но этот вопрос достаточно внятно описан в документации этих методов. Мне не хотелось тратить время читателя на «простые вещи», описанные в документации. В том числе на такие, как std::atomic::is_lock_free и std::atomic_flag.
Пожалуйста, Хорошего кодинга на leetcode!
Спасибо, что заметили. Поменяю слова. Вообще «понятный», «простой для понимания» — это слишком субъективно.
Имена полей от куда брать?
Да, C++ разработчики любят написать свой std::string, не спорю =) Думаю, это связано с любовью копанием в регистрах низкоуровневых мелочах, и отсутствием нормального менеджера зависимостей.

Насчет вашего выпада про готовые решения: библиотеки, которые вы указали — это парсеры json/ini и прочего. Условно, из json строки в map<string, any>. Это первая часть балета. Вторая часть — маппинг map<string, any> в конкретную составную структуру C++.

Именно вторую задачу решает автор статьи. Можно было бы хотя бы прочитать заголовок: «Отображение данных в формате json на структуру C++». По сути, это элементы рефлексии полей структуры с различными аттрибутами (имя поля json, дефолт и пр.).

Если знаете библиотеку, которая это делает — дайте ссылку.

EDIT: danielaparker.github.io/jsoncons имеет возможность маппинга json в структуру, но отсутствуют фичи, которые полезны для работы с конфигами (дефолтные значения, валидация параметров, логирование прочитанного конфига и пр.).
Спасибо за статью и за то, что показали свое решение. Совсем недавно я искал решение для парсинга и дампинга иерархических конфигов (вложенные классы, вектора, мапки, ...) из/в json. К моему удивлению, а не смог найти готового решения. Поэтому написал свой, как и вы.

Ваше решение «do the job», но имеет несколько недостатков, на мой взгляд:
  1. магия макросов. Это значит возможный конфликт имен (или очень длинные имена), трудность понимания этого кода и сложности для IDE
  2. определение структуры и ее парсера смешано в одном коде. Это значит, что мы не можем сделать парсер для уже готовой структуры, не можем дать значения по-умолчанию
  3. не поддерживаются std контейнеры (как я понял), сложность расширения парсера на свои типы (например std::chrono::duration)


Позволю тут оставить ссылку на свое решение: github.com/dmitryikh/rcfg

Основная идея — мы описываем парсер структуры (в том числе с вложенными полями) обычным C++ кодом. Далее использую объект парсера можно читать/писать в/из json, валидировать параметры, логировать поля, которые были обновлены.

Небольшой пример:
// Destination config
struct Config
{
    std::string dir;
    uint64_t severity;
    bool feature;
    std::string name;
    double velocity;
    std::string password;
};

// Initialize parser with rules
auto GetParser()
{
    rcfg::ClassParser<Config> p;
    p.member(&Config::dir, "Dir", rcfg::NotEmpty);
    p.member(&Config::severity, "Severity", rcfg::Bounds{0, 6}, rcfg::Default{4});
    p.member(&Config::feature, "Feature");
    p.member(&Config::name, "Name", rcfg::NotEmpty, rcfg::Default("MyName"s), rcfg::Updatable);
    p.member(&Config::velocity, "Vel", rcfg::Bounds{0.0, 100.0});
    // secret means that the field value won't be revealed after reading
    p.member(&Config::password, "Password", rcfg::NotEmpty, rcfg::Secret);
    return p;
}
Спасибо за статью. Лаконично и понятно написано.

Указанный способ — хороший пример того, как сделать типобезопасный type erasure в C++ без интерфейсов и наследования.
Странный тест на C++.
В одном из примеров присходит присваивание по неинициализированному указателю `*b = a`. После этого бессмысленно спрашивать, что выведет программа — да хоть розовых пони!

Пример с reinpreted_cast — тоже UB. Посмотрите www.youtube.com/watch?v=_qzMpk-22cc
На удивление, VisualCode очень плавно работает и ресурсов не так много ест. Хотя тоже, как я понимаю, построен на web технологиях.
Спасибо за ссылку, я не пробовал database_exporter, но выглядит так, что он делает похожие вещи.
Чем nerve отличается:
1. Плагинами можно расширять типы задач. В частности у нас есть задачи, которые достают метрики из HTTP-запросов. А тип задачи CompareQueries делает запросы в разные базы и сравнивает результаты (only in left, only in right, ...).
2. nerve выполняет запросы по расписанию — у каждого запроса своя периодичность. Это позволяет не грузить БД и накапливать необходимую статистику.
3. Сам запрос может быть шаблонизирован (используются jinja2 шаблоны), например конструкция {{scheduled_time | delta("-2h") | toDateTime}} будет заменена на валидный для базы данных формат даты и времени
  1. Можно ли использовать Bioyino в одном экземпляре, без балансировщика и консула?
  2. Выбор rust'а — это решение команды/компании, или это выбор конкретного сотрудника для своего pet-проекта? Планируете ли еще писать на rust в Avito?
1

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность