MacGamingFixes
macgamingfixes / отсрочка записи реестра

отсрочка записи реестра

часть macgamingfixes. та же статья есть в приложении, рядом с переключателем.

wine дважды в минуту замораживает все процессы бутылки примерно на четверть секунды, чтобы переписать реестр. этот фикс придерживает запись, пока идёт игра.

симптом

каждые 30 с игра подтормаживает. на сетевом графике 15–17 тиков приходят одной пачкой. то же самое происходит на loopback, так что wi-fi ни при чём.

причина

wineserver однопоточный. каждые 30 с он в этом единственном потоке целиком переписывает каждую ветку реестра, изменившуюся с прошлого сохранения, одним текстовым файлом. в бутылке со steam system.reg весит около 12 мб (36.5 тыс. ключей), поэтому одна запись блокирует все обращения к серверу и все события сокетов примерно на 240 мс.

ветка не остаётся чистой надолго: steam раз в минуту трогает HKLM\Software\Wow6432Node\Valve\SteamService.

замеры

udp-эхо на 64 гц по loopback. приёмник работает внутри игровой бутылки так же, как сетевой код valve (WSAEventSelect + recvfrom). запущен deadlock, 110–120 с на прогон.

приёмникp99максимумопоздали > 5 мс
нативный1.2 мс8 мс2 / 7040
wine, без фикса6.1 мс264 мс80 / 7040
wine, с фиксом3.1 мс10 мс18 / 7680
самый долгий фриздо264 мспосле10 мс

четыре фриза без фикса длились 232–264 мс, шли ровно через 30 с и заканчивались в пределах 7 мс от момента перезаписи system.reg.

фикс

в wineserver внедряется маленькая библиотека, которая перехватывает open(). когда начинается периодическая запись ветки больше 1 мб, а сервер занят (его доля cpu выше 5 %), открытие временного файла сразу завершается ошибкой. ветка остаётся изменённой, wineserver повторяет попытку через 30 с, и запись проходит, когда бутылка затихает. запись при выходе и запись маленьких веток вроде user.reg не затрагиваются.

wineserver_fix.c
if (branch[0] && periodic) {
    double share = cpu_share();
    if (busy_cpu > 0 && share > busy_cpu) {  // идёт игра
        errno = EACCES;                      // save_branch() отступает, ветка остаётся изменённой
        return -1;
    }
}
return open(path, flags, mode);

что меняется на диске

проверка

поиграйте несколько минут и откройте журнал. во время игры каждые 30 с должна появляться строка defer system.reg.

tail -f ~/Library/Logs/mgf/regsave.log

ограничения