Профайлер (под Win32) показал, что бОльшую часть времени ЯОС занимается ленивым подметанием (LazySweep). Пытаемся для начала понять,
какую статистику можно собрать об этом процессе, например, посчитать количество актов выделения памяти, количество сборок мусора, количество действий по поиску нужного блока.
пытаемся ускорить выделение памяти
- Лис [Вежливый]
- Сообщения: 563
- Зарегистрирован: 08.10.18 13:32
Re: пытаемся ускорить выделение памяти
Скорость выделения в правильных GC не зависит от скорости сборки.
А скорость сборки ограничена сверху и не должна превышать 50% всей вычислительной мощности.
Какой тогда смысл экономить электричество?
Тут одно из двух, либо что-то неясно, либо GC неправильный.
А скорость сборки ограничена сверху и не должна превышать 50% всей вычислительной мощности.
Какой тогда смысл экономить электричество?
Тут одно из двух, либо что-то неясно, либо GC неправильный.
Re: пытаемся ускорить выделение памяти
https://gitlab.com/budden/ja-o-s/-/blob ... s.Mod#L844 - там ленивое подметание, поэтому зависит.
Но дело оказалось не в том, а тупо в том, что при замере длины литеры UCS32 (а в ЯОС используются нуль-терминированные строки, это ужасно, но переделывать, очевидно, нет ресурсов) на каждую литеру UCS32 вызывалась функция.
Стоило убрать вызов - поиск ускорился сразу в 10 раз.
Профайлер промахивается и показывал узкое место не там, где надо.
Хотя ускорения в 10 раз недостаточно, нужно в 100.
Но дело оказалось не в том, а тупо в том, что при замере длины литеры UCS32 (а в ЯОС используются нуль-терминированные строки, это ужасно, но переделывать, очевидно, нет ресурсов) на каждую литеру UCS32 вызывалась функция.
Стоило убрать вызов - поиск ускорился сразу в 10 раз.
Профайлер промахивается и показывал узкое место не там, где надо.
Хотя ускорения в 10 раз недостаточно, нужно в 100.
Re: пытаемся ускорить выделение памяти
Действовал по мотивам https://forum.oberoncore.ru/viewtopic.p ... 6&p=112980, где сказано, что для просмотра сведений о куче нужно запустить WMPerfMonPluginHeap.Install ~ и посмотреть в Системное меню->Диагностика->Производительность->Plugins
Там я смотрел на скорость выделения блоков в секунду, на общий объём выделенных блоков и их распределение по размерам.
Было две версии:
- дело в блокировках, которые нужно захватить при каждом выделении - в этом случае должна быть абсолютно ограничено к-во выделений в секунду, причём независимо от размера кучи
- дело в большом общем количестве блоков, что замедляет LazySweep (как перевести - ленивое подметание?) - в этом случае скрость должна была падать по мере роста кучи
Путём запуска тяжёлых задач, таких, как компиляция многих файлов или открытие среды разработки со многими вкладками, я убедился, что обе гипотезы не выдерживают критики. Тогда я заподозрил, что профайлер "мажет" и показывает места, соседние с узкими. Я стал искать, что он ещё показывает, и убедился, что он недоволен функцией UCSStrings32.Substring и UCSStrings32.Length. Тут я последовательно обнаружил и вычистил две вещи:
- при измерении длины строки UCS32 на каждую литеру UCS32 вызывается функция - это я заменил на обращение к полю записи, и поиск ускорился в 10 раз.
- в функции Substring исключительно для проверки корректности выходных параметров измеряется длина строки. А мы выкусываем лексемы прямо из
файла целиком, поэтому эта строка является длинной. Здесь я сделал версию ф-ии substring, которая не меряет длину строки, и поиск ускорился ещё раз в 5.
Там я смотрел на скорость выделения блоков в секунду, на общий объём выделенных блоков и их распределение по размерам.
Было две версии:
- дело в блокировках, которые нужно захватить при каждом выделении - в этом случае должна быть абсолютно ограничено к-во выделений в секунду, причём независимо от размера кучи
- дело в большом общем количестве блоков, что замедляет LazySweep (как перевести - ленивое подметание?) - в этом случае скрость должна была падать по мере роста кучи
Путём запуска тяжёлых задач, таких, как компиляция многих файлов или открытие среды разработки со многими вкладками, я убедился, что обе гипотезы не выдерживают критики. Тогда я заподозрил, что профайлер "мажет" и показывает места, соседние с узкими. Я стал искать, что он ещё показывает, и убедился, что он недоволен функцией UCSStrings32.Substring и UCSStrings32.Length. Тут я последовательно обнаружил и вычистил две вещи:
- при измерении длины строки UCS32 на каждую литеру UCS32 вызывается функция - это я заменил на обращение к полю записи, и поиск ускорился в 10 раз.
- в функции Substring исключительно для проверки корректности выходных параметров измеряется длина строки. А мы выкусываем лексемы прямо из
файла целиком, поэтому эта строка является длинной. Здесь я сделал версию ф-ии substring, которая не меряет длину строки, и поиск ускорился ещё раз в 5.
Re: пытаемся ускорить выделение памяти
В итоге теперь поиск по всем исходникам занимает порядка 30 секунд (а VS Code делает такой поиск практически мгновенно). А изначально только по одному файлу искало 30 секунд, а файлов 700. Если так считать, то может быть, что поиск ускорился не в 50 раз, а в 500, но суть не в том, а в том, что всё равно 30 секунд - это кажется слишком долго.
Теперь мы опять упираемся в выделение памяти, хотя другие вещи уже проступают и ускорить более чем в 3 раза уже никак не выйдет.
Собственно говоря, один из вариантов ускорения состоит в том, чтобы реализовать "срезы массивов" как в Go, тогда нагрузку на кучу можно снизить. Например, вместо того, чтобы создавать строчки в лексемах, можно брать срезы. Хотя тогда нужно и сами лексемы хранить в виде массива, а не массива указателей, а здесь мы упрёмся в ограничения языка, т.к. у нас контейнер реализован на массиве указателей, а без дженериков придётся вручную реализовывать контейнер на массиве записей.
Короче говоря, как бы не пришлось смириться с этой ситуацией...
Теперь мы опять упираемся в выделение памяти, хотя другие вещи уже проступают и ускорить более чем в 3 раза уже никак не выйдет.
Собственно говоря, один из вариантов ускорения состоит в том, чтобы реализовать "срезы массивов" как в Go, тогда нагрузку на кучу можно снизить. Например, вместо того, чтобы создавать строчки в лексемах, можно брать срезы. Хотя тогда нужно и сами лексемы хранить в виде массива, а не массива указателей, а здесь мы упрёмся в ограничения языка, т.к. у нас контейнер реализован на массиве указателей, а без дженериков придётся вручную реализовывать контейнер на массиве записей.
Короче говоря, как бы не пришлось смириться с этой ситуацией...