Обновить
4
0.1
Сергей@gres_84

C++ Developer

Отправить сообщение

Язык мироздания — теория групп и теория категорий

Уровень сложностиСредний
Время на прочтение11 мин
Охват и читатели11K

Около полутора лет назад я опубликовал на Хабре статью под названием "Слово Божие — функциональное программирование как основа Вселенной", в которой я рассказывал про лямбда-исчисление и про то, как программу любой сложности можно свести к алгоритму на базе всего трёх SKI-комбинаторов или же одного единственного йота-комбинатора. В ней мы разобрались с алфавитом божественного языка, на котором написана книга мироздания. Теперь же пришло время разобраться с его грамматикой.

Читать далее

RAII 2.0: RAII как архитектурный инструмент в C++

Уровень сложностиСредний
Время на прочтение7 мин
Охват и читатели3.5K

Идиома RAII — давно зарекомендовал себя как удобный способ автоматического управления ресурсами в C++. Обычно мы применяем его для управления памятью, файловыми дескрипторами или мьютексами. Однако что, если расширить понятие RAII до управления не только физическими ресурсами, но и логическими контрактами и состояниями системы?

В этой статье я хочу поговорить о том, как RAII можно использовать для контроля жизненного цикла асинхронных операций, транзакций или подписок, гарантируя их корректное завершение или откат до прежнего состояния.

Читать далее

Game++. Work hard

Уровень сложностиПростой
Время на прочтение15 мин
Охват и читатели2.3K

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

Модели выполнения используются разные — от простой многопоточности с ручной синхронизацией до более продвинутых систем акторов, job-based подходов или task graph. Например, системы поведения ИИ могут обновляться параллельно с физикой, пока основной поток отвечает за рендеринг. Некоторые движки, такие как Unreal Engine, используют task graph (граф задач), где зависимости между задачами выражаются явно, и задачи автоматически распределяются по доступным ядрам. Другие подходы, как в CryEngine Perth (аналог ECS, матрица задач), позволяют организовать данные так, чтобы минимизировать ложные зависимости и повысить кэш-эффективность. Конечный выбор всегда зависит от архитектуры движка, платформы и требований конкретной задачи или группы задач.

Читать далее

Хеш-таблица и C++20

Уровень сложностиСложный
Время на прочтение6 мин
Охват и читатели5.4K

Рассматриваем различные «приколюхи» из C++20 на примере хеш‑мапы!

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

Читать далее статьи от @SEIka69

Как правильно готовить std::span

Время на прочтение6 мин
Охват и читатели4.3K

Сегодня мы поговорим про std::span и как не порезаться на острых углах C++.

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

Вообще, с учётом того, что, начиная с С++17 мы уже знакомы с понятием string_view, можно представить, что std::span — это нечто подобное, только действующее для непрерывных участков памяти, которые ещё можно и модифицировать. Но не будем забегать вперед, обо всех свойствах по-порядку.

Читать далее

Game++. Unpacking containers

Уровень сложностиПростой
Время на прочтение40 мин
Охват и читатели2.5K

Независимо от того, начинаете ли вы разрабатывать свою игру или присоединяетесь к уже существующему проекту, когда приходит время оптимизировать память и заниматься разным улучшайзингом, то всегда встают одни и те же вопросы. Стоит ли использовать собственные контейнеры? Если использовать свои, то какой лучше выбрать - похожий на vector, или больше подойдет map? Является ли связный список наилучшим выбором при частых вставках и удалениях элементов? А откуда эти вставки вообще взялись, но это конечно другой вопрос.

В большинстве случаев студии начинают реализовывать свои решения, заточенные под игру, это со временем приводит к появлению библиотеки решений, а частенько и полной замене всего STL стека. Основная причина - это добиться непрерывного размещения элементов в памяти, чтобы максимизировать локальность кэша при их обходе. Надеюсь, понятно для чего это делают: часто быстрее обойти 1000 элементов, которые лежат друг за другом, чем дюжину, которая раскидана по разным частям оперативки.

Если вы не готовы писать и поддерживать свою STL, старайтесь, использовать vector, он хотя бы предсказуем по времени на всех платформах. Так вам скажет большинство разработчиков игр на C++, но проблема в том, что vector перераспределяет хранимые объекты в памяти при вставке новых элементов, а также при удалении любого элемента, кроме последнего. Это означает, что указатели на элементы вектора становятся недействительными, и тогда все зависимости и взаимодействия между элементами перестают работать.

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

Читать далее

Стековые канарейки и где они обитают. Приручаем один из ключевых харденингов

Уровень сложностиСредний
Время на прочтение11 мин
Охват и читатели1.6K

Хабр, привет! Меня зовут Мария Недяк, я специализируюсь на разработке харденингов нашей собственной микроядерной операционной системы «Лаборатории Касперского» KasperskyOS. Если вкратце: мы стараемся сделать любые атаки на нашу ОС невозможными — или хотя бы очень дорогими :-)

Один из главных инструментов в нашей нелегкой работе — «канарейка» (ну или Stack Canary), которая защищает от базовой атаки переполнения стека. Лично я к работе с этой птичкой уже давно привыкла — набила руку во время многократных CTF-турниров, где без такого харденинга было никуда… Этот бэкграунд очень пригодился мне в «Лаборатории Касперского», когда перед нашей командой встала задача усилить «канарейку» в KasperskyOS.

В статье я подробно объясню, как работает Stack Canary, как ее ломают — и как от этих методов взлома защититься. Сразу скажу: тема непростая, так что для самых любопытных я оставила список полезной литературы в конце текста. Поехали!

Читать далее

Планировщик Go — самый подробный гайд простым языком

Уровень сложностиСредний
Время на прочтение27 мин
Охват и читатели39K

Давайте спроектируем с нуля планировщик Go — начнём с самой простой и понятной наивной реализации, а затем шаг за шагом будем разбираться, какие изъяны в ней есть, и придумывать как их решать, постепенно усложняя общую модель.

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

Приступить к проектированию

Фрактальное самоподобие Вселенной и бесконечная вложенность материи – правда или миф?

Уровень сложностиСредний
Время на прочтение22 мин
Охват и читатели8.2K

В наши дни вряд ли найдется человек, который не созерцал залипательные изображения, гифки или видео с фракталами. Они завораживают своей красотой, гармоничными пропорциями и масштабной инвариантностью – сохранением узнаваемой формы при отдалении и приближении. В эзотерической литературе довольно часто можно встретить утверждение, что всё в природе имеет фрактальную структуру, а значит, за этим стоит универсальный принцип всеединства и великого фрактального подобия. Или же существует некий глубинный уровень реальности, а то, что мы видим – лишь отражение мира идей, фрактальная голограмма. Тут же обязательно вспоминают золотое сечение, герметическую формулу «как вверху, так и внизу», мысли античных философов о подобии микрокосма и макрокосма, сходство нейронных связей мозга с паутиной галактических скоплений и т.д. И каждый, кто не поленится сравнить компьютерные модели фракталов с реальными контурами береговых линий или кронами деревьев, невольно начинает верить в сакральную геометрию, высшую гармонию природы и бесконечное самоподобие космоса. Мы же подойдём к вопросу более трезво и выясним, что из этого является правдой, а что – мифами.

Читать далее

Алгоритмы манипуляций с битами

Уровень сложностиСредний
Время на прочтение13 мин
Охват и читатели10K

в статье приведены алгоритмы обработки коротких битовых строк, обычно вмещающихся в машинное слово, в большей степени эти алгоритмы предназначены для обработки строк длины 32 или 64, но многие из них можно применять для SIMD инструкций или даже GPU.

Читать далее

Пишем графический ASCII-калькулятор с помощью стандартной библиотеки Си

Уровень сложностиСложный
Время на прочтение17 мин
Охват и читатели19K

Программа calculator.c родилась как школьный проект в рамках Student Innovation Scholarship. Сперва я решил написать простой инструмент для построения графиков функций с помощью символов ASCII, но после завершения первого прототипа понял, что задача намного сложнее, чем предполагалось. Вернувшись к проекту год спустя, я увидел, что в нём есть много неочевидных нюансов. Поэтому предлагаю разобрать весь процесс разработки моего графического калькулятора с нуля.


Читать дальше →

Как уместить поиск по 30 тысячам слов в 64 КБ ОЗУ

Уровень сложностиСредний
Время на прочтение17 мин
Охват и читатели6.1K

Как уместить словарь размером 250 КБ в 64 КБ ОЗУ с возможностью выполнения быстрого поиска? Для справки: даже современные методики сжатия наподобие gzip -9 не могут сжать этот файл до размера меньше 85 КБ.

В 1970-х Дуглас Макилрой столкнулся с этой непростой задачей при реализации проверки правописания для Unix в AT&T. Из-за ограничений компьютера PDP-11 весь словарь должен был умещаться всего в 64 КБ ОЗУ. Кажется, подобную задачу решить невозможно.

Вместо того, чтобы использовать стандартные методики сжатия, Дуглас воспользовался преимуществами свойств данных, разработав алгоритм сжатия, отличавшийся от теоретического минимума сжатия всего на 0,03 бита. И по сей день этот рекорд остаётся непревзойдённым.

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

Читать далее

C++26 — встреча ISO в Хагенберге

Уровень сложностиСредний
Время на прочтение8 мин
Охват и читатели15K
В середине февраля в Хагенберге состоялась встреча международного комитета по стандартизации языка программирования C++.



В этот раз прорабатывались следующие большие темы:
  • std::hive
  • Constexpr, ещё больше constexpr
  • Безопасность, контракты, hardening, профили, UB и std::launder
  • Relocate
  • #embed

Об этом и других новинках расскажу в посте

Game++. Juggling STL algorithms

Уровень сложностиПростой
Время на прочтение29 мин
Охват и читатели2.5K

Мы все пишем циклы, в каждом софте, в каждой игре они будут. Вы не можете обойтись без них. Скажете, что делает этот код?

stl::vector numbers = {1, 2, 3, 4, 5};
int sum = 0;
for (int num : numbers) {
sum += num;
}

Конечно, это просто: код суммирует элементы массива. Похожую задачу про суммирование или другую операцию над массивом мой лид даёт на собесах :) Люди смотрят с удивлением, а потом большинство пишут, вот то, что было выше. И тут три вещи - человек либо поленился прочитать про STL алгоритмы, либо не доверяет нам и знает про них, но думает что не поймем мы, либо знает, но не понимает зачем показываеть эти знания, почему? вопрос оставим открытым. Этот пример с циклом - простейший алгоритм.

Алгоритмы STL — это настоящий швейцарский нож для разработчика. Они не просто помогают писать код, а делают его чище, понятнее и надежнее. В проектах с большими кодовыми базами, где легаси код не всегда стабилен и удобен для поддержки, это особенно важно. Каждый, кто писал циклы вручную, сталкивался с ошибками: вылезли за границы массива, забыли обработать пустой контейнер, сделали лишнее копирование. STL-алгоритмы избавляют от многих проблем, позволяя выразить мысли кратко и четко. Вместо простыней кода с индексами — несколько строк с понятным смыслом. Так что, если вы еще не знакомы со стандартными алгоритмами, самое время это исправить. Это один из тех инструментов, которые однажды освоив, уже невозможно забыть, это как езда на велосипеде, хорошем промышленном велике, за авторством Кнута или Саттера - надежном и с серийным номером.

Читать далее

Game++. Dancing with allocators

Уровень сложностиПростой
Время на прочтение34 мин
Охват и читатели6.8K

C и C++ не имеют встроенной сборки мусора, поэтому разработчик сам решает, как и когда выделять и освобождать память. Мы, конечно, можем покивать в сторону STL, сокрытия аллокаций в контейнерах, но от этого они никуда не денутся. Просто если раньше приходилось думать про выделенный кусок памяти, понимать, как он скажется на времени фрейма, помнить, что его надо удалить (а может, не надо и стоит оставить на следующий фрейм), то теперь всё заворачивается в сахарные контейнеры и разработку в стиле STL-blin-vse-sterpit. STL-то может и стерпит, и даже как-то будет ворочаться, однако не стоит полагаться исключительно на системный аллокатор, бездумно вызывая new или malloc для каждого запроса памяти. Вы ведь понимаете, что std::vector посреди цикла или горячей функции — это плохая идея?

Кроме того, такая практика приводит к ожидаемым проблемам с производительностью даже в обычных приложениях, чего уж говорить про высоконагруженные системы или игры, которые претендуют на что-то быстрее 20 фреймов в секунду.

Пытаться оптимизировать код, который использует системные аллокаторы, — всё равно что сгребать листья в кучу ветреным днём: куча, конечно, сгребается, но постоянно приходится махать грабельками, чтобы она оставалась на одном месте. Даже если выделения памяти происходят последовательно, друг за другом, вот прям без всяких перерывов, нет гарантии, что эти участки будут расположены хотя бы близко друг к другу. В результате при обработке таких данных процессору приходится прыгать по разным участкам памяти, теряя такты просто на поиск данных вместо того, чтобы работать с ними.

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

Ребята из HFT, Database, Automotive и Embedded-систем наверняка могут рассказать немало интересных историй про оптимизацию new/delete. Давайте я расскажу немного про разные аллокаторы в играх?

Аллокатор аллокатору аллокации аллоцировал

Game++. Cooking vectors

Уровень сложностиПростой
Время на прочтение12 мин
Охват и читатели4.3K

В разработке игр динамические и статические массивы являются основным инструментом при работе с набором объектов, буду дальше называть их vector. Вы можете подумать про разные map, set, и другие ускоряющие структуры, но их тоже предпочитают делать поверх векторов. Почему так? Вектора просты для понимания, удобны для большого числа задач, особенно там, где объём данных заранее неизвестен или примерно известен. Но как вы понимаете, за все надо платить, и расплачиваться приходится производительностью, которой, как обычно, всегда не хватает. Так что, использование динамических массивов имеет свои ограничения и особенности.

Читать далее

Game++. String interning

Уровень сложностиПростой
Время на прочтение9 мин
Охват и читатели5.4K

«String interning», иногда это называют «пулом строк» — это оптимизация (https://en.wikipedia.org/wiki/String_interning), при которой хранится только одна копия строки, независимо от того, сколько раз программа ссылается на нее. Среди других оптимизаций по работе со строками (SWAR, SIMD-cтроки, immutable strings, StrHash, Rope string, и немного других), часть которых была описана тут, она считается одной из самых полезных оптимизаций в игровых движках, есть правда небольшие недостатки у этого подхода, но экономия памяти и скорость работы при правильной подготовке ресурсов и работе с лихвой их перекрывают.

Вы 100% когда-нибудь писали одну и ту же строку несколько раз в одной программе. Например:pcstr color = "black"; А позже в коде пришлось написать: strcmp(color, "black");Как видите, строковый литерал "black" встречается несколько раз. Означает ли это, что программа содержит две копии строки "black"? Более того, означает ли это, что в оперативную память загружаются две копии этой строки? На оба вопроса ответ — зависит от компилятора и вендора. Благодаря некоторым оптимизациям в сlang (Sony) и GCC, каждая строка-литерал хранится в программе только в одном экземпляре, и, следовательно, только одна копия загружается в оперативную память, поэтому иногда cтановятся возможными разные фокусы.

Просто не копируй это...

Game++. run, thread, run…

Уровень сложностиПростой
Время на прочтение33 мин
Охват и читатели3.9K

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

В обычном программировании с блокировками, когда возникает необходимость пошарить данные, приходится использовать механизмы сериализации доступа к таким данным, чтобы операции, выполняющие работу с такими данными, были ограничены от одновременного вмешательства со стороны других потоков и возможности их поломать. В прямом смысле поломать. Даже такая простая операция, как ++count, где count имеет тип integer, требует блокировки, поскольку операция инкремента в общем случае представляет собой трехшаговую операцию (чтение, модификация, запись), которая не является атомарной. Про что-то более сложное и длительное я уже и не говорю.

За кажущейся простотой скрывается множество граблей и ловушек: взаимные блокировки (deadlock), «голодание» потоков, асинхронные ошибки. Это похоже на попытку дирижировать оркестром, где музыканты игнорируют ритм. Проще говоря, любые действия над данными могут привести к проблемам, и чтобы этого не происходило, операции над данными должны быть атомарными, это решается вводом в код примитивов синхронизации, вроде мьютексов, семафоров, спинлоков.

Первая хорошая сторона программирования с блокировками состоит в том, что пока ресурс заблокирован, никакая другая логика не может вмешаться. Вторая хорошая сторона — люди прекрасно понимают, читают и работают с таким кодом, потому что он хорошо вписывается в "естественное" понимание устройства мира. А вот дальше начинаются проблемы...

Читать далее

Простейшие алгоритмы сжатия: RLE и LZ77

Время на прочтение9 мин
Охват и читатели160K
Давным-давно, когда я был ещё наивным школьником, мне вдруг стало жутко любопытно: а каким же волшебным образом данные в архивах занимают меньше места? Оседлав свой верный диалап, я начал бороздить просторы Интернетов в поисках ответа, и нашёл множество статей с довольно подробным изложением интересующей меня информации. Но ни одна из них тогда не показалась мне простой для понимания — листинги кода казались китайской грамотой, а попытки понять необычную терминологию и разнообразные формулы не увенчивались успехом.

Поэтому целью данной статьи является дать представление о простейших алгоритмах сжатия тем, кому знания и опыт пока ещё не позволяют сходу понимать более профессиональную литературу, или же чей профиль и вовсе далёк от подобной тематики. Т.е. я «на пальцах» расскажу об одних из простейших алгоритмах и приведу примеры их реализации без километровых листингов кода.
Читать дальше →

Спинлок в современном C++ с применением атомиков, барьеров памяти и экспоненциальной выдержкой

Время на прочтение9 мин
Охват и читатели2.8K
Эта статья послужит вам быстрым, но глубоким введением в низкоуровневую конкурентность.

Механизмы блокировки — важнейшая часть конкурентного программирования. Такие механизмы позволяют множественным потокам одновременно обращаться к разделяемым ресурсам, не мешая друг другу. Одна из самых популярных блокировок – это спинлок (циклическая блокировка), при которой применяется активное ожидание, механизм, позволяющий раз за разом проверять, не освободилась ли блокировка. Правда, при таком подходе будут тратиться драгоценные такты процессора, если блокировка зациклится и станет впустую потреблять ресурсы процессора. Для решения этой проблемы применяется подход под названием экспоненциальная выдержка. При экспоненциальной выдержке применяются постепенно нарастающие периоды ожидания, что позволяет не тратить ресурсы впустую.

В этой статье мы реализуем наш собственный упрощённый спинлок с экспоненциальной выдержкой. Для начала обсудим базовую идею, на которой основан спинлок — проблему активного ожидания. Затем разберём, что представляет собой экспоненциальная выдержка и обсудим, как повысить эффективность спинлоков. Затем поговорим об атомиках и о том, для чего они используются. После этого объясним, что представляют собой барьеры памяти, если они работают в тандеме. Далее рассмотрим образец реализации спинлока с экспоненциальной выдержкой, разберём достоинства и недостатки такого подхода. Наконец, напишем тестовую программу, которая поможет нам убедиться, что всё работает как надо. Начнём!
Читать дальше →

Информация

В рейтинге
3 251-й
Откуда
Москва, Москва и Московская обл., Россия
Дата рождения
Зарегистрирован
Активность