#c #data-structures #hashtable
#c #структуры данных #хэш-таблица
Вопрос:
Я хочу создать карту таким образом, чтобы набор указателей указывал на массивы динамического размера. Я использовал хеширование с цепочкой. Но поскольку данные, для которых я их использую, огромны, программа выдает std::bad_alloc
после нескольких итераций. Причина, по которой может быть new
использован для создания связанного списка.
Кто-нибудь, пожалуйста, подскажите, какую структуру данных мне использовать? Или что-нибудь еще, что может улучшить использование памяти с помощью моей хэш-таблицы?
Программа написана на C .
Вот как выглядит мой код: инициализация хэш-таблицы:
class Link
{
public:
double iData;
Link* pNext;
Link(double it) : iData(it)
{ }
void displayLink()
{ cout << iData << " "; }
};
class List
{
private:
Link* pFirst;
public:
List()
{ pFirst = NULL; }
void insert(double key)
{
if(pFirst==NULL)
pFirst = new Link(key);
else
{
Link* pLink = new Link(key);
pLink->pNext = pFirst;
pFirst = pLink;
}
}
};
class HashTable
{
public:
int arraySize;
vector<List*> hashArray;
HashTable(int size)
{
hashArray.resize(size);
for(int j=0; j<size; j )
hashArray[j] = new List;
}
};
основной фрагмент:
int t_sample = 1000;
for(int i=0; i < k; i ) // initialize random position
{
x[i] = (cal_rand() * dom_sizex); //dom_sizex = 20e-10 cal_rand() generates rand no between 0 and 1
y[i] = (cal_rand() * dom_sizey); //dom_sizey = 10e-10
}
for(int t=0; t < t_sample; t )
{
int size;
size = cell_nox * cell_noy; //size of hash table cell_nox = 212, cell_noy = 424
HashTable theHashTable(size); //make table
int hashValue = 0;
for(int n=0; n<k; n ) // k = 10*212*424
{
int m = x[n] /cell_width; //cell_width = 4.7e-8
int l = y[n] / cell_width;
hashValue = (kx*l) m;
theHashTable.hashArray[hashValue]->insert(n);
}
-------
-------
}
Комментарии:
1. Итак, вам нужно всего хранить ~ 900 миллионов значений в памяти? Даже если это всего лишь 4 байта на значение, у вас закончится доступное виртуальное адресное пространство для 32-разрядной программы. Никакое изменение структуры данных не может это исправить.
2. @T.C. но люди выполняют такие симуляции на Fortran. Нет ли какой-либо возможности справиться с этим?
3. @aks: хорошо, вы могли бы прокомментировать, нужно ли вам сохранять все значения из каждой итерации одновременно или есть какой-то способ обработки и удаления некоторых из этих результатов ранее? В зависимости от ваших потребностей в доступе к данным вы можете рассмотреть возможность настройки хорошего объема пространства подкачки или явной записи значений на диск до тех пор, пока они снова не понадобятся. Кроме того, вы можете скомпилировать 64-разрядное приложение? Сколько оперативной памяти у вас есть в наличии?
4. @aks Итак, у вас действительно есть только ~ 900 тыс. значений, и вы обновляете их только на каждой итерации, а не создаете новые? Тогда вам, вероятно, нужно искать утечки памяти в вашем коде.
5. Как говорит T.C., если он проходит через несколько итераций, а затем выходит из строя, это говорит о том, что у него было достаточно памяти для начальной итерации, но впоследствии произошла утечка. В более общем плане,
std::unordered_map<key, std::vector<value>>
звучит примерно так, если у вас нет, например, непрерывных увеличивающихся ключей — тогда вы можете просто иметьvector<value>
. Если разница между минимальной и максимальной длиной очень мала, вы можете рассмотреть astd::array<>
с начальным элементом длины или завершающим элементом sentinel .
Ответ №1:
Перво-наперво, используйте стандартный контейнер. В вашем конкретном случае вы можете захотеть:
- либо
std::unordered_multimap<int, double>
- или
std::unordered_map<int, std::vector<double>>
(Примечание: если у вас нет C 11, они доступны в Boost)
Ваш основной цикл становится (используя второй вариант):
typedef std::unordered_map<int, std::vector<double>> HashTable;
for(int t = 0; t < t_sample; t)
{
size_t const size = cell_nox * cell_noy;
// size of hash table cell_nox = 212, cell_noy = 424
HashTable theHashTable;
theHashTable.reserve(size);
for (int n = 0; n < k; n) // k = 10*212*424
{
int m = x[n] / cell_width; //cell_width = 4.7e-8
int l = y[n] / cell_width;
int const cellId = (kx*l) m;
theHashTable[cellId].push_back(n);
}
}
Это не приведет к утечке памяти (надежно), хотя, конечно, у вас могут быть другие утечки, и, таким образом, даст вам надежную базовую линию. Это также, вероятно, быстрее, чем ваш подход, с более удобным интерфейсом и т.д…
В общем, вам не следует заново изобретать колесо, если только у вас нет конкретной потребности, которая не удовлетворяется доступными колесами, или вы на самом деле пытаетесь узнать, как создать колесо или создать лучшее колесо.
Ответ №2:
ОС должна решать те же проблемы со страницами памяти, может быть, стоит посмотреть, как это делается? Прежде всего, давайте предположим, что все страницы находятся на диске. Страница — это фрагмент памяти фиксированного размера. Для вашего варианта использования, допустим, это массив ваших записей. Поскольку объем оперативной памяти ограничен, ОС поддерживает сопоставление между номером страницы и ее местоположением в оперативной памяти.
Итак, допустим, на ваших страницах 1000 записей, и вы хотите получить доступ к записи 2024, вы должны запросить у ОС страницу 2 и прочитать запись 24 с этой страницы. Таким образом, ваша карта имеет размер всего 1/1000.
Теперь, если ваша страница не имеет сопоставления с ячейкой памяти, то она либо находится на диске, либо к ней никогда раньше не обращались (пуста). Затем вам нужно заменить другую страницу и загрузить эту страницу с диска (и обновить отображение местоположения).
Это очень упрощенное описание того, что происходит, и я не удивлюсь, если кто-то набросится на меня за такое описание.
Дело в том,:
Что это значит для вас?
Во-первых, ваши данные превышают объем оперативной памяти — вы не обойдетесь без записи на диск, если не хотите сначала попробовать сжатие. Во-вторых, ваши цепочки могут работать как страницы, если хотите, но мне интересно, будет ли лучше работать просто подкачка вашего хэш-кода. Я имею в виду, что используйте верхние биты в качестве номера страницы, а младшие биты — в качестве смещения на странице. По-прежнему важно избегать столкновений, так как вы хотите загрузить как можно меньше страниц. Вы все равно можете объединить свои страницы в цепочку и в итоге получить карту гораздо меньшего размера. Вторая важная часть — решить, какие страницы поменять местами, чтобы освободить место для новых страниц. LRU должен работать нормально. Если вы можете лучше предсказать, какие страницы вам (не) понадобятся, тем лучше для вас. В-третьих, вам нужны заполнители для ваших страниц, чтобы указать, находятся ли они в памяти или на диске.
Надеюсь, это поможет.
Комментарии:
1. Извините, но большая часть этого отскочила от моей головы. Позвольте мне перефразировать мой вопрос. Просто знайте, что я на самом деле пытаюсь сделать со своим кодом. У меня 8988880 частиц, 89888 ячеек. Итак, 10 частиц в каждой ячейке. Теперь, поскольку я случайным образом распределяю позицию для этих частиц, мне нужно проиндексировать каждую частицу в ее ячейке. Итак, я сопоставил 10 значений с ячейкой, используя связанный список. И ячейки отмечены хэш-значениями хэш-таблицы. Когда я смоделировал эту систему с более чем 200 итерациями, программа завершилась следующим образом: terminate вызывается после создания экземпляра ‘std::bad_alloc’
2. Итак, что это за ячейки, пространственные позиции / объемы? Зачем вообще использовать хэш-таблицы, простого массива недостаточно? Из того, что я понял из вашего кода, ваши итерации независимы друг от друга? Разве вы не можете просто сохранять результаты каждой итерации на диск перед началом следующей и удалять их из памяти?
3. вы имеете в виду, что я должен инициализировать 89888 массивов? я использовал это для удаления: for(int m=0; m () { Link pCurrent = pFirst; while (pCurrent != NULL) { Link* del = pCurrent; pCurrent = pCurrent-> pNext; свободно (del); } pFirst = NULL; }
4. Кроме того, всегда ли в ячейке 10 частиц или это просто ожидаемое значение? Удаление выглядит нормально для меня, но, честно говоря, у меня больше опыта работы с Java.
5. ни один 10 не является средним. он будет меняться от ячейки к ячейке.