2015-10-06 7 views
1

Если у меня есть сшитая структура, как это:Cache местность с большими структурами в C

struct phonebook { 
    char LastName[16]; 
    char FirstName[16]; 
    char Email[16]; 
    char PhoneNumber1[10]; 
    char PhoneNumber2[10]; 
    char Addr1[16]; 
    char Addr2[16]; 
    char City[10]; 
    char Country[12]; 
    char State[2]; 
    struct phonebook *pNext; 
} 

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

while (pHead != NULL) { 
    if (strcasecmp(lastname, pHead->LastName) == 0) 
      return pHead; 
    pHead = pHead->pNext; 
} 

return NULL; 

что-то например, но каждый раз, когда я получаю узел телефонной книги, кеш загружает всю структуру, а кеш пропускает много.
Итак, как я могу увеличить скорость попадания в кеш?
Как получить сгруппированный LastName s в кеше?

Без горячего/холодного или перерыв связанного списка в цепочку хеш-таблицы.

+0

См. [Этот вопрос re AoS versus SoA] (http://stackoverflow.com/questions/5323154/which-kind-of-data-organization-using-c-arrays-makes-fastest-code-and-why/5323220 # 5323220). –

+1

Когда вы говорите: «кеш будет загружать всю структуру, а кеш пропустит много», это то, что вы предполагаете или результат фактического профилирования? Весьма маловероятно (хотя и не совсем невозможно), что приложение телефонной книги будет чувствительным к производительности, чтобы заметно пострадать от пропусков в кеше. – ach

+3

Когда вы используете алгоритм поиска O (n), нет смысла беспокоиться о производительности кэша. Это сложная проблема, потому что «правильный» ответ как с точки зрения кеша, так и с точки зрения оперативной сложности заключается в использовании хеш-таблицы. – QuestionC

ответ

1

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

Если ваша куча нефрагментирована, когда вы создаете свой список, общее пространство памяти для структуры, вероятно, будет смежным, даже если отдельные узлы не являются (предполагается, что вы вставляете узлы в середину цепочки с некоторой частотой). Если ваша куча фрагментирована в этот момент, список будет распространяться намного дальше.

Если вы используете фрагментированную кучу, но знаете, насколько велика будет связанный список, вы можете предварительно выделить большой блок памяти при запуске программы и распределить ее по мере необходимости. Это потенциально опасно для ОЗУ, но уменьшает промахи в кэше и случай, когда вы выделили узлы против уже фрагментированной кучи.

TCMalloc также может обеспечить улучшение скорости попадания в кеш, поскольку он достаточно эффективен для использования в небольших объемах. Он также пытается сохранить последовательные небольшие выделения на одной странице памяти 4K.

Как улучшить способ поиска

Если связанный список отсортирован, он сортируется только по одному критерию. Вы можете сохранить отдельную структуру данных (например, хеш-таблицу), которая сопоставляет конкретный ключ поиска (например, LastName + FirstName) указателю для этого узла в связанном списке. Это концептуально похоже на то, как база данных имеет кластерный индекс, представляющий физическое упорядочение строк и потенциально несколько некластеризованных индексов для разных критериев поиска (поиск по электронной почте, телефону, имени).

+0

Предварительное выделение делает их более плотными в ОЗУ. Но мне все равно нужно загрузить другие части (и т. Д. FirstName, Addr1, Addr2 ...) в кеш. Мисс по-прежнему высока, когда мне нужна небольшая часть информации в этой структуре. – tonyyanxuan

+0

В качестве незначительного улучшения вы можете посмотреть, как ваша структура будет упакована. Ваши массивы могут начинаться с 32-битных или 64-битных границ с использованием настроек по умолчанию. Учитывая ваши текущие размеры полей, упаковка в байты или границы слов немного уменьшит размер каждой структуры, немного улучшив локальность кеша. –

0

каждый раз, когда я получаю узел телефонной книги, кеш загружает всю структуру, а кеш пропускает много. Итак, как я могу увеличить скорость попадания в кеш? Как получить сгруппированные LastNames в кеше?

C требует, чтобы элементы каждого структурного объекта были выложены последовательно (но не обязательно смежно) в памяти. Поэтому да, массивы LastName различных структур распространяются в памяти, даже если вы считаете, что сами структуры не могут быть смежными. Вы не можете изменить это, поскольку это указано C.

Вы можете, однако, создать индекс, состоящий из динамического массива мелких структур, таких как

struct pb_index { 
    char LastName[16]; 
    struct phonebook *entry; 
} 

В LastName массивы будут более плотными внутри этого динамического массива, чем в массиве phonebook с, так сканирование через такой массив позволит более эффективно использовать кеш, чем сканирование через ваш связанный список.

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

+0

Я думаю, что это своего рода метод «горячий/холодный»? Что делать, если я не хочу структуры? Можно ли сделать это, сделайте массив имен более плотным. – tonyyanxuan

+0

@tonyyanxuan Вы можете изменить структуру 'phonebook', или вы можете использовать другую структуру вообще (как в моем ответе), но вы не можете использовать существующую структуру без изменений для достижения более высокой плотности' LastName 'в пределах общего массива или любой части памяти, назначенной ему. Как я уже сказал, C указывает, что все члены одной структуры должны быть выложены в памяти как группа, поэтому между каждой парой 'LastName' из соседних объектов struct phonebook будут все остальные члены 'struct phonebook' , и, возможно, некоторые дополнения. –

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