2012-04-20 9 views
3

У меня есть программа на C++, которая использует несколько очень больших массивов двойников, и я хочу уменьшить объем памяти этой конкретной части программы. В настоящее время я выделяю 100 из них, и они могут быть по 100 Мб каждый.Как управлять большими массивами

Теперь у меня есть то преимущество, что в конечном итоге части этих массивов становятся устаревшими во время последующих частей выполнения программы, и в любом случае нет необходимости когда-либо иметь в памяти все из них в памяти.

Мой вопрос заключается в следующем:

Есть ли способ сказать ОС после того, как я создал массив с новыми или таНос, что часть ненужно больше? Я прихожу к выводу, что единственный способ достичь этого - объявить массив указателей, каждый из которых может указывать на кусок, скажем 1 Мб нужного массива, так что старые куски, которые не нужны никакие больше можно повторно использовать для новых бит массива. Мне кажется, что я пишу пользовательский менеджер памяти, который кажется немного кувалдой, и это также создаст небольшой удар производительности.

Я не могу перемещать данные в массиве, потому что он собирается чтобы вызвать слишком много проблем с конфликтом. к массивам можно получить доступ к любому из большого количества потоков в любое время, хотя только один поток когда-либо записывает в любой заданный массив.

+0

Часто ответ на проблемы с очень большим массивом заключается в использовании * разреженных массивов * - структуры данных, которая выглядит как массив, но на самом деле хранит только соответствующие элементы. Это работает, если большая часть массива - пустое пространство. Это правда здесь, или в какой-то момент вам действительно нужны все элементы в данном массиве? –

+0

Будет ли приемлемым решение для платформы? Posix позволяет вам «mmap» большой регион, а затем «munmap» его части, как только вы закончите с ними. –

ответ

4

Это зависит от операционной системы. POSIX - включая Linux - имеет системный вызов madvise, чтобы улучшить производительность памяти. Из справочной страницы:

Системный вызов madvise() сообщает ядру о том, как обрабатывать ввод/вывод подкачки в диапазоне адресов, начиная с адреса addr и с байтами длины. Он позволяет приложению сообщать ядру, как он рассчитывает использовать некоторые отображаемые или разделяемые области памяти, чтобы ядро ​​могло выбирать подходящие методы чтения и кэширования. Этот вызов не влияет на семантику приложения (за исключением MADV_DONTNEED), но может влиять на его производительность. Ядро может игнорировать совет.

Дополнительную информацию см. На странице руководства пользователя madvise.

Редактировать: По-видимому, приведенное выше описание было недостаточно ясным. Итак, вот несколько подробностей, и некоторые из них специфичны для Linux.

Вы можете использовать mmap для выделения блока памяти (непосредственно из ОС вместо libc), который не поддерживается никаким файлом. Для больших кусков памяти malloc делает то же самое. Вы должны использовать munmap, чтобы освободить память - независимо от использования madvise:

void* data = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, 
    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 
// ... 
::munmap(data, size); 

Если вы хотите, чтобы избавиться от некоторых частей этого фрагмента, вы можете использовать madvise, чтобы сообщить ядру сделать так:

madvise(static_cast<unsigned char*>(data) + 7 * page_size, 
    3 * page_size, MADV_DONTNEED); 

Диапазон адресов по-прежнему действителен, но он больше не поддерживается - ни по физической памяти, ни по хранению. Если позже вы перейдете к страницам, ядро ​​будет выделять некоторые новые страницы «на лету» и повторно инициализировать их до нуля. Имейте в виду, что страницы dontneed также являются частью объема виртуальной памяти процесса. Возможно, потребуется внести некоторые изменения конфигурации в управление виртуальной памятью, например. активирование over-commit.

+1

Вы не должны «освобождать» или «удалять» память после этого; который может попытаться повторно использовать память, которая больше не доступна для процесса. –

+0

Просто просматривайте страницу man для mmap. каковы преимущества вызова с MAP_NORESERVE против MAP_ANONYMOUS или обоих? – camelccc

+1

MAP_NORESERVE относится к перегрузке памяти, то есть будет ли начальное распределение памяти успешным, хотя может случиться, что оно не может быть предоставлено позже. Вы не должны использовать MAP_NORESERVE, если вы планируете немедленно использовать память. – nosid

0

Вы можете попробовать использовать списки вместо массивов. Конечно, список «тяжелее», чем массив, но, с другой стороны, легко восстановить список, чтобы вы могли отбросить часть его, когда он устарел. Вы также можете использовать обертку, которая будет содержать только индексы, указывающие, какая часть списка обновлена ​​и какая часть может быть повторно использована. Это поможет вам повысить производительность, но потребует немного больше (многоразовой) памяти.

+0

, однако, списки не должны быть доступны в последовательности. Итерация через список для доступа к элементу 1000000+ будет медленным – camelccc

+0

Если только одна часть списка используется одновременно, это будет не очень медленным. Фактически, это зависит от реализации списка. Список в std реализован очень оптимально, и производительность не должна быть намного медленнее. – superM

1

Было бы легче ответить, если бы у нас было больше деталей.

1 °) Ответ на вопрос «Есть ли способ сообщить OS после того, как я создал массив с новым или malloc, что часть его больше не нужна?» «на самом деле». Это точка C и C++ и любой язык, который позволяет обрабатывать память вручную.

2 °) Если вы используете C++, а не C, вы не должны использовать malloc.

3 °). Не массивы, если по очень конкретной причине. Используйте std :: vector.

4 °) Предпочтительно, если вам необходимо часто менять содержимое массива и уменьшать объем памяти, используйте связанный список (std :: list), хотя будет более дорого «индивидуально» получить доступ к содержимое списка (но будет почти таким же быстрым, если вы только перебираете его).

1

std::deque с указателями на std::array<double,LARGE_NUMBER> может выполнять эту работу, но лучше сделать выделенный контейнер с deque, чтобы вы могли переназначить индексы и, самое главное, определить, когда записи больше не используются.

Конкретный контейнер также может содержать блокировку чтения/записи, поэтому его можно использовать поточно-безопасным способом.

0

Выделение по кусочкам и delete[] -е и new[]-на пути похоже хорошее решение. Возможно, возможно сделать как можно меньше управления памятью. Не используйте повторно кусок самостоятельно, просто освободите старый и при необходимости выделите новые куски.

Смежные вопросы