Причина ~100x замедляется с функциями памяти "кучи" с помощью HEAP_NO_SERIALIZE на Windows Vista и Windows 7

Я пробую к tracedown огромное замедление в функциях памяти "кучи" в Windows Vista и Windows 7 (я не протестировал ни на каких выпусках сервера). Этого не происходит на Windows XP вообще, только в более новых операционных системах Microsoft.

Я первоначально столкнулся с этой проблемой с PHP, соответствовавшим в Windows. Сами сценарии, казалось, работали на ожидаемой скорости, но после выполнения сценария я испытывал 1-2 секунды задержки внутренних функций завершения работы PHP. После разжигания отладки я видел, что это имело отношение к использованию диспетчера памяти PHP HeapAlloc/HeapFree/HeapReAlloc.

Я проследил его вниз до использования флага HEAP_NO_SERIALIZE на функциях "кучи":

#ifdef ZEND_WIN32
#define ZEND_DO_MALLOC(size) (AG(memory_heap) ? HeapAlloc(AG(memory_heap), HEAP_NO_SERIALIZE, size) : malloc(size))
#define ZEND_DO_FREE(ptr) (AG(memory_heap) ? HeapFree(AG(memory_heap), HEAP_NO_SERIALIZE, ptr) : free(ptr))
#define ZEND_DO_REALLOC(ptr, size) (AG(memory_heap) ? HeapReAlloc(AG(memory_heap), HEAP_NO_SERIALIZE, ptr, size) : realloc(ptr, size))
#else
#define ZEND_DO_MALLOC(size) malloc(size)
#define ZEND_DO_FREE(ptr) free(ptr)
#define ZEND_DO_REALLOC(ptr, size) realloc(ptr, size)
#endif

и (который на самом деле устанавливает значение по умолчанию для HeapAlloc/HeapFree/HeapReAlloc) в функции start_memory_manager:

#ifdef ZEND_WIN32
    AG(memory_heap) = HeapCreate(HEAP_NO_SERIALIZE, 256*1024, 0);
#endif

Я удалил HEAP_NO_SERIALIZE параметр (замененный 0), и это решило проблему. Сценарии теперь очистка быстро и в CLI и в версии SAPI Apache 2. Это было для PHP 4.4.9, но PHP 5 и 6 исходных кодов (в разработке) содержат тот же флаг на вызовах.

Я не уверен, было ли то, что я сделал, опасно или нет. Это - весь часть диспетчера памяти PHP, таким образом, я оказываюсь перед необходимостью делать некоторое рытье и исследование, но это поднимает вопрос:

Почему функция памяти "кучи" является настолько медленной на Windows Vista и Windows 7 с HEAP_NO_SERIALIZE?

При исследовании этой проблемы я придумал точно один хороший хит. Считайте сообщение в блоге http://www.brainfarter.net/?p=69, где плакат объясняет проблему и предлагает тестовый сценарий (и источник и доступные двоичные файлы) для выделения проблемы.

Мои тесты в Windows 7 x64 четырехъядерная машина на 8 ГБ дают 43,836. Ай! Те же результаты без HEAP_NO_SERIALIZE флаг 655, ~70x быстрее в моем случае.

Наконец, кажется, что любая программа создала с Visual C++ 6 использований malloc/free или new/delete кажется, затронут на этих более новых платформах. Компилятор Visual C++ 2008 года не устанавливает этот флаг по умолчанию для тех функций/операторов, таким образом, они не затронуты - но это все еще оставляет много программ затронутым!

Я поощряю Вас загружать подтверждение концепции и давать этому попытку. Эта проблема объяснила, почему мой нормальный PHP на установке Windows был сканирования и может объяснить, почему Windows Vista и Windows 7 кажутся медленнее время от времени.

ОБНОВЛЕНИЕ 26.01.2010: Я получил ответ от Microsoft, заявив, что слабо фрагментируемая "куча" (LFH) является фактической политикой по умолчанию для "кучи", которая содержит любое заметное количество выделений. В Windows Vista они реорганизовали много кода для удаления дополнительных структур данных и путей выполнения кода, которые больше не были частью общего падежа для обработки вызовов API "кучи". С HEAP_NO_SERIALIZE флаг и в определенных ситуациях с отладкой, они не позволяют использование LFH, и мы застреваем на медленном и менее оптимизированном пути через диспетчер "кучи". Так... это настоятельно рекомендовано для не использования HEAP_NO_SERIALIZE так как Вы пропустите всю работу к LFH и любую будущую работу в "куче" Windows API.

12
задан Peter Mortensen 24 January 2013 в 19:38
поделиться

1 ответ

Первое отличие, которое я заметил, заключается в том, что Windows Vista всегда использует кучу низкой фрагментации (LFH). Windows XP, похоже, нет. RtlFreeHeap в Windows Vista в результате намного короче - вся работа делегирована RtlpLowFragHeapFree . Дополнительная информация о LFH и его присутствии в различных ОС. Обратите внимание на красное предупреждение вверху.

Дополнительная информация (раздел примечаний):

Windows XP, Windows Server 2003 и Windows 2000 с исправлением KB 816542:

Вспомогательный список - это быстрая память. механизм распределения, содержащий только блоки фиксированного размера. Смотреть в сторону списки по умолчанию включены для куч которые их поддерживают. Начиная с Windows Vista, дополнительные списки не используется, а LFH включен по умолчанию .

Еще одна важная информация: LFH и NO_SERIALIZE являются взаимоисключающими (оба не могут быть активными одновременно). В сочетании с

Начиная с Windows Vista, вспомогательные списки не используется

Это означает, что установка NO_SERIALIZE в Windows Vista отключает LFH, но не (и не может) возвращаться к стандартным вспомогательным спискам (в качестве быстрой замены) , согласно приведенной выше цитате. Мне неясно, какую стратегию распределения кучи использует Windows Vista, когда указано NO_SERIALIZE . Судя по производительности, похоже, что он использует что-то ужасно наивное.

Дополнительная информация:

Глядя на несколько снимков стека allocspeed.exe , кажется, что он всегда находится в состоянии готовности (не выполняется или ожидает), а также в TryEnterCriticalSection из HeapFree и привязка процессора к почти 100% загрузке в течение 40 секунд. (В Windows Vista.)

Пример снимка:

ntdll.dll!RtlInterlockedPushEntrySList+0xe8
ntdll.dll!RtlTryEnterCriticalSection+0x33b
kernel32.dll!HeapFree+0x14
allocspeed.EXE+0x11ad
allocspeed.EXE+0x1e15
kernel32.dll!BaseThreadInitThunk+0x12
ntdll.dll!LdrInitializeThunk+0x4d

Что странно, потому что NO_SERIALIZE точно сообщает ему пропустить получение блокировки . Что-то не сходится.

На этот вопрос могли ответить только Раймонд Чен или Марк Руссинович :)

11
ответ дан 2 December 2019 в 22:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: