Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.22.70.9] |
|
Сообщ.
#1
,
|
|
|
Разрабатываю программы решения Задачи Коммивояжера (ЗК) на языке Си в среде Qt. Операционные системы: Linux, Windows.
Особенность ЗК - для решения этой задачи большой размерности (например, 300 городов) часто бывает мало 8-16-32-64Гб RAM. Особенно нетерпимо - сброс огромных массивов данных, по которым идет расчет, из RAM в SWAP. Вот и возникает задача: как определить максимально доступный объем непрерывной динамической памяти? Поиск в Интернете привел к созданию такого фрагмента кода для достижения указанной цели: // Операционная система определяется макросом WINVER (есть в Windows и нет в Linux) #ifndef WINVER #include <sys/sysinfo.h> #else #include <sysinfoapi.h> #endif // Переменные для информации об основных параметрах ОС (главным образом - RAM). Заодно определяется "слэш" в файловой системе #ifndef WINVER struct sysinfo osInfo; char cSl = '/'; #else MEMORYSTATUSEX osInfo; char cSl = '\\'; #endif unsigned long long int // Общий объем доступной непрерывной динамической RAM, шаг при поиске iMemMax iMemMax, iDMem; char // Указатель, "нарезающий" выделяемую память для массивов данных, используемых при решении ЗК *pByte; // Чтение информации о состоянии операционной системы #ifndef WINVER sysinfo(&osInfo); iMemMax = osInfo.freeram; // "Ефрейторский" зазор - настолько пошагово уменьшается память, если ее нельзя выделить iDMem = osInfo.totalram / 1000 + 1; #else osInfo.dwLength = sizeof(osInfo); if (!GlobalMemoryStatusEx(&osInfo)) { // osInfo.freeram; printf("Не могу определить размер динамической памяти!"); return -1; } iMemMax = osInfo.ullAvailPhys; iDMem = osInfo.ullTotalPhys / 1000 + 1; #endif pByte = (char *)calloc(iMemMax, sizeof(char)); while (!pByte && (iMemMax > iDMem)) { iMemMax -= iDMem; pByte = (char *)calloc(iMemMax, sizeof(char)); } if (!pByte) { printf("Не могу выделить динамическую память!\n"); return -1; } #ifndef WINVER printf("Динамической памяти "); printf("RAM = %lld Mbyte\n", iMemMax / 1048576); // 1048576 = 1024 *1024 #else printf("Динамической памяти "); printf("RAM = %I64d Mbyte\n", iMemMax / 1048576); // 1048576 = 1024 *1024 #endif В чем состоит проблема? Как это ни странно, в Windows-7 32bit проблем нет: программа в Qt (компиляция realese) выделяет 1600Мб RAM, а в mingw32 (ключ -O3) выделяет 1860Мб RAM. Все проблемы с Linux. Когда многократно запускаешь скомпилированную программу, сначала идет все хорошо (пусть будет 8Гб физической RAM): например, при первом запуске выделяется блок в 7000Мб RAM; при втором запуске (пример) выделяется уже 7200Мб RAM; и так далее - при каждом следующем запуске ОС освобождает все больше RAM для решения Задачи Коммивояжера. Заканчивается все тем, что из 8192Мб физической RAM выделяется, скажем, 7860Мб RAM и при этом часть памяти начинает сидеть в SWAP! То есть начинает использоваться SWAP на HDD! Визуально это наблюдается так: HDD начинает перманентно "трещать". Трещит и 5 минут, и 10 минут, и 20 минут... Мне приходится делать вот что: начиная с некоторого момента перед запуском программы решения ЗК приходится запускать LibreOffice только с одной целью - чтобы офис частично занял RAM (после запуска ЗК я Офис закрываю). Вопрос, собственно, такой: как в Linux сделать так, чтобы выделяемый блок памяти (близкий к максимальному) сидел четко в RAM? Может мой код надо как-то подрегулировать? (Замечания и предложения, разумеется, принимаются). |
Сообщ.
#2
,
|
|
|
Строго говоря, дело тут не в Windows или Linux, дело в разрядности приложения. В 64-биной винде будет то же самое.
Задача, в общем-то неоднозначная. Предположим, ты определил эффективный для тебя размер региона памяти. Ок, тебя ОСь свопить не будет... но ведь в системе есть и другие процессы, и оставив им очень мало памяти, ты заставишь ОСь свопать их, и они, получая управление даже изредка, всё равно будут деградировать производительность системы в целом, а значит и твою тоже. Готовых рецептов тут просто не может быть, т.к. золотая середина завист от факторов, которые достоверно просчитать в общем случае просто невозможно. Добавлено Я бы предложил сначала просто получить регион максимального размера, в WinAPI это VirtualAlloc(). Так ты получишь потенциально самый большой размер региона, который только возможно, но в 64-х битах наверняка сповиться он будет безбожно. Затем попробовать поколдовать над размерами рабочего пространства посредством SetProcessWorkingSetSize(). Так ты сможешь получить эффективный размер региона, который система может позволить тебе оставлять резидентным в памяти, хотя и без гарантии этой резидентности и ценой деградации производительности системы в целом. И наконец залочить регион в памяти с помощью VirtualLock(). Так ты получишь максимально большой, но всё ещё разумный по размеру регион памяти, который не будет свопаться. После этого можно VirtualFree() декоммитить "лишние" страницы, отдав т.о. избыточно отхапанную память на другие нужды и ... посмотреть, что из этого алгоритма получилось. Что и как из этого будет применимо в Linux, без понятия. Добавлено Естественно, подбор этих максимумов надо делать разумно, например, дихотомией. Добавлено P.S. Я тут по быстрому накидал нечто вот такое (внимание! синтетика! в продакшне как есть не использовать!): #define NOMINMAX #include <windows.h> #include <iostream> #include <limits> int main() { size_t size = std::numeric_limits<decltype(size)>::max(), oldSize = size; void *mem; do { mem = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (mem == NULL) { oldSize = size; size >>= 1; continue; } size_t newSize = (oldSize >> 1) + (size >> 1); oldSize = size; size = newSize; VirtualFree(mem, 0, MEM_RELEASE); } while (size != oldSize); size_t allocated = size; mem = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (mem == NULL) return std::cout << "Ops!\n", 1; std::cout << "Allocated " << size << " bytes" << std::endl; do { if (SetProcessWorkingSetSize(GetCurrentProcess(), size, size) == FALSE) { oldSize = size; size >>= 1; continue; } size_t newSize = (oldSize >> 1) + (size >> 1); oldSize = size; size = newSize; } while (size != oldSize); if (SetProcessWorkingSetSize(GetCurrentProcess(), size, size) == FALSE) return std::cout << "Ops 2!\n", 2; std::cout << "Residented " << size << " bytes" << std::endl; do { if (VirtualLock(mem, size) == FALSE) { oldSize = size; size >>= 1; continue; } size_t newSize = (oldSize >> 1) + (size >> 1); oldSize = size; size = newSize; VirtualUnlock(mem, oldSize); } while (size != oldSize); if (VirtualLock(mem, size) == FALSE) return std::cout << "Ops 3!\n", 3; std::cout << "Locked " << size << " bytes" << std::endl; SetProcessWorkingSetSize(GetCurrentProcess(), size, size); VirtualFree((char*)mem + size, allocated - size, MEM_DECOMMIT); std::cout << "Ok" << std::endl; // <- тут бряк VirtualUnlock(mem, oldSize); SetProcessWorkingSetSize(GetCurrentProcess(), -1, -1); VirtualFree(mem, 0, MEM_RELEASE); } Allocated 1431655763 bytes Residented 1431655762 bytes Locked 954437164 bytes Allocated 1431655763 bytes Residented 477218569 bytes Locked 318145701 bytes Ok Добавлено Естественно, эксперимент не чистый, процесс под анализом стоит и ничего не делает. Ну и буду дома, проверю ещё на 64-х битах с 16Гб. |
Сообщ.
#3
,
|
|
|
Результаты на 64-х битах в сходных условиях: 16Гб на i5 с одним нагруженным тем же тяжёлым скриптом ядром:
Allocated 22906492243 bytes Residented 11453246118 bytes Locked 7635497391 bytes Allocated 17179869181 bytes Residented 1610612733 bytes Locked 1073741819 bytes P.S. На момент запуска первого экземпляра память была утилизирована на 42%, так что можно было ожидать ~9Гб залоченной памяти, но система оказалась умней. |