Выше, чем ожидалось использование памяти с VirtualAlloc; что происходит?
Важно : прокрутите вниз до "окончательного обновления", прежде чем тратить здесь слишком много времени. Оказывается, главный урок состоит в том, чтобы остерегаться побочных эффектов других тестов в вашем наборе unittest и всегда воспроизводить вещи изолированно , Прежде чем делать поспешные выводы!
На первый взгляд, следующий 64-битный код выделяет (и обращается) к одной мега-4K страницам с помощью VirtualAlloc (всего 4 Гбайт):
const size_t N=4; // Tests with this many Gigabytes
const size_t pagesize4k=4096;
const size_t npages=(N<<30)/pagesize4k;
BOOST_AUTO_TEST_CASE(test_VirtualAlloc) {
std::vector<void*> pages(npages,0);
for (size_t i=0;i<pages.size();++i) {
pages[i]=VirtualAlloc(0,pagesize4k,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
*reinterpret_cast<char*>(pages[i])=1;
}
// Check all allocs succeeded
BOOST_CHECK(std::find(pages.begin(),pages.end(),nullptr)==pages.end());
// Free what we allocated
bool trouble=false;
for (size_t i=0;i<pages.size();++i) {
const BOOL err=VirtualFree(pages[i],0,MEM_RELEASE);
if (err==0) trouble=true;
}
BOOST_CHECK(!trouble);
}
Однако при его выполнении растет "Рабочий набор" сообщается в Диспетчере задач Windows (и подтверждается значением "прилипание" в столбце "пиковый рабочий набор") от базового ~200 000 K (~200 Мбайт) до более чем 6 000 000 или 7 000 000 K (тестируется на 64-битном Windows7, а также на ESX-виртуализированном 64-битном сервере 2003 и Server 2008; к сожалению, я не обратил внимания, на каких системах наблюдались различные числа).
Еще один очень похожий тестовый случай в тех же unittest исполняемых тестах one-mega 4k mallocs (далее освобождает), и это только расширяется примерно на ожидаемые 4 Гбайт при запуске.
Я не понимаю: у VirtualAlloc есть какие-то довольно высокие накладные расходы на аллок? Очевидно, что это значительная часть размера страницы, если это так; зачем нужно так много дополнительного и для чего это нужно? Или я неправильно понимаю, что на самом деле означает" рабочий набор"? Что здесь происходит?
Update : со ссылкой на ответ Ганса, я отмечаю, что это не удается с нарушением доступа на второй странице доступа, так что все, что происходит, не так просто, как распределение, округленное до 64K "детализации".
char*const ptr = reinterpret_cast<char*>(
VirtualAlloc(0, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)
);
ptr[0] = 1;
ptr[4096] = 1;
Update : Теперь на экземпляре AWS / EC2 Windows2008 R2 с установленным VisualStudioExpress2013 я не могу воспроизвести проблему с этим минимальным кодом (скомпилированным 64-битным), который завершается, по-видимому, свободным от накладных расходов пиковым рабочим набором 4,335,816 K, который я ожидал увидеть изначально. Так что либо есть что-то другое в других машинах, которые я запуск На, или Boost-test на основе exe, используемого в предыдущем тестировании. Биззаро, Продолжение следует...
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <vector>
int main(int, char**) {
const size_t N = 4;
const size_t pagesize4k = 4096;
const size_t npages = (N << 30) / pagesize4k;
std::vector<void*> pages(npages, 0);
for (size_t i = 0; i < pages.size(); ++i) {
pages[i] = VirtualAlloc(0, pagesize4k, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
*reinterpret_cast<char*>(pages[i]) = 1;
}
Sleep(5000);
for (size_t i = 0; i < pages.size(); ++i) {
VirtualFree(pages[i], 0, MEM_RELEASE);
}
return 0;
}
Последнее обновление : извинения! Я бы удалил этот вопрос, если бы мог, потому что оказалось, что наблюдаемые проблемы были полностью из-за непосредственно предшествующего unittest в тестовом наборе, который использовал "масштабируемый распределитель" TBB, чтобы выделить/освободить пару Гбайт материала. Похоже, что масштабируемый распределитель фактически сохраняет такие распределения в своем собственном пуле вместо того, чтобы возвращать их в систему (см., например, здесь или здесь). Стало очевидным, как только я запустил тесты индивидуально с достаточным количеством Sleep
после них, чтобы наблюдать их рабочий набор по завершению в диспетчере задач (можно ли что-нибудь сделать с поведением TBB, может быть, интересный вопрос, но как-это вопрос здесь является отвлекающим маневром).
2 ответа:
pages[i]=VirtualAlloc(0,pagesize4k,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
Вы не получите 4096 байт,это будет округлено до наименьшего разрешенного выделения. Это SYSTEM_INFO.dwAllocationGranularity, он был 64KB в течение длительного времени. Это очень простой счетчик фрагментации адресного пространства.
Таким образом, вы выделяете Путь больше, чем вы думаете.
Оказывается, что наблюдаемые проблемы былиполностью из-за непосредственно предшествующего unittest в тестовом наборе, который использовал "масштабируемый распределитель" TBB, чтобы выделить/освободить пару Гбайт материала. Похоже, что масштабируемый распределитель фактически сохраняет такие распределения в своем собственном пуле, а не возвращает их в систему (см., например, здесь или здесь). Стало очевидным, как только я провел тесты индивидуально с достаточным количеством
Sleep
после них, чтобы наблюдать их работу по завершению установите в диспетчере задач.