perf: dirty-tracking + пул + LPT для сохранения клановых сундуков (#3165)#3185
Merged
perf: dirty-tracking + пул + LPT для сохранения клановых сундуков (#3165)#3185
Conversation
Компонент ClanSystem::IngrChestSaver владеет пулом потоков и множеством изменённых сундуков. Экземпляр живёт в GlobalObjects рядом с остальным глобальным состоянием. Заголовок минимальный: mark_dirty(Clan*) и run(). Все детали (пул, unordered_set, логика сохранения) скрыты в приватном Impl -- изменения реализации не тянут за собой перекомпиляцию файлов, включающих заголовок. Алгоритм run(): - собирает задачи по пересечению Clan::ClanList и dirty-множества; - снимает флаг dirty с клана при постановке задачи; параллельный put_ingr_chest взведёт его заново, и следующий вызов run() подхватит новое состояние; - сортирует задачи по убыванию get_ingr_chest_objcount() (LPT: время сериализации линейно зависит от числа объектов, крупнейшая задача стартует первой); - ставит задачи в постоянный utils::ThreadPool, ждёт результатов; - при ошибке записи возвращает dirty-флаг, чтобы следующий вызов повторил попытку. Clan::put_ingr_chest, Clan::take_ingr_chest, Clan::purge_ingr_chest помечают клан через GlobalObjects::ingr_chest_saver().mark_dirty(this). ClanSystem::save_ingr_chests -- тонкая обёртка над run(), вызовы из heartbeat и hcontrol save не меняются. Issue: #3165
a25e889 to
c2730bd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Контекст
Issue #3165.
После PR #3181 сохранение клановых сундуков с ингредиентами на боевом сервере не укладывается в бюджет одного heartbeat-пульса (
kPassesPerSec = 25, то есть 40 мс на пульс):Главный поток блокируется на
WaitAll()примерно на 50 мс — пульс не успевает.Что в PR
Подсистема сохранения клановых ингредиентных сундуков вынесена в отдельный компонент.
Новый компонент
ClanSystem::IngrChestSaverФайлы
src/gameplay/clans/ingr_chest_saver.{h,cpp}. Заголовок минимальный:Pimpl: пул потоков,
std::unordered_set<Clan*>, логика сериализации и LPT-сортировка — всё в приватномImpl, спрятано за указателем. Изменения реализации не вызывают перекомпиляцию пользователей заголовка.Экземпляр в
GlobalObjectsПоле
ClanSystem::IngrChestSaver ingr_chest_saverвGlobalObjectsStorage+ геттерGlobalObjects::ingr_chest_saver(). В той же куче, чтоtrigger_list,world_objects,zone_table,heartbeatи прочее глобальное состояние — явно и видно.ClanSystem— этоnamespaceсо свободными функциями, положить пул туда как поле нельзя без переделки всегоnamespaceвclass. Переделка пространства имён в класс — отдельная большая задача (лучше в рамках #3180 про глобалы), здесь не трогаю.Dirty-tracking
Clan::put_ingr_chest,Clan::take_ingr_chest,Clan::purge_ingr_chestвызываютGlobalObjects::ingr_chest_saver().mark_dirty(this). Других путей изменить содержимое сундука с ингредиентами в коде нет (проверено: только эти три функции управляют спискомchest->contains, DG-скрипты прямого доступа не имеют).В
Clanнет ни поля, ни методов для отслеживания изменений — набор грязных сундуков живёт внутриIngrChestSaver::Impl.LPT-сортировка
Время сериализации одного сундука линейно зависит от числа объектов в нём. Вес задачи —
Clan::get_ingr_chest_objcount(), никаких syscall'ов не делаем. Сортировка задач по убыванию: самая большая стартует первой, wall time близок к нижней границеmax(longest, total / threads).Постоянный пул потоков
utils::ThreadPoolживёт внутриIngrChestSaver::Implи переиспользуется. Раньше пул создавался и уничтожался на каждый вызовsave_ingr_chests, что давало лишниеpthread_create+joinкаждые 10 минут.ClanSystem::save_ingr_chestsТонкая обёртка над
GlobalObjects::ingr_chest_saver().run(). Вызовы из heartbeat иhcontrol saveне менялись.Ожидаемый эффект
Оценка на боевом сервере (6 активных сундуков, 2 ядра):
Clan::ClanList+ проверка в unordered_set)max(их размеров), обычно <30 мсВ часы низкой активности большинство вызовов
run()будут пустыми.Изменения
src/gameplay/clans/ingr_chest_saver.{h,cpp}— новый компонент.src/engine/db/global_objects.{h,cpp}— поле + геттер.src/gameplay/clans/house.cpp:put_ingr_chest,take_ingr_chest,purge_ingr_chest—mark_dirty(this).save_ingr_chestsужата до одной строки вызоваrun().save_one_ingr_chestи прежнее телоsave_ingr_chests(они теперь вIngrChestSaver::Impl).CMakeLists.txt— регистрация новых файлов.Заголовок
house.hне меняется, то есть для всех включающих его файлов пересборка не требуется.Безопасность многопоточности
write_one_object(после PR #3181) читаетObjDataбез мутаций. Рабочие потоки пула только читают объекты, не трогаютdirty-множество.mark_dirtyиrunвызываются из главного потока. Никаких атомиков и мьютексов не нужно.Test plan
make tests && ./tests/tests— 372 теста зелёные.make circle— собирается.wall <0.001и отдельные рабочие циклы сwall <0.030для 1–2 сундуков.