стратегия выделить/освободить много маленьких объектов

Я играю с определенным алгоритмом кэширования, который сложен несколько. В основном это должно выделить много маленьких объектов (двойные массивы, 1 - 256 элементов), с объектами, доступными через отображенное значение, map[key] = array. время к инициализированному массиву может быть довольно большим, обычно больше чем 10 тысяч циклов CPU.

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

Какова была бы хорошая стратегия избежать фрагментации памяти, в то время как тихое поддержание разумного выделяет, освобождают скорость?

Я использую C++, таким образом, я могу использовать новый и malloc.Спасибо.

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

моей платформой разработки является Intel Xeon, операционная система Linux. Идеально я хотел бы работать над PPC Linux также, но это не является самым важным для меня.

7
задан Community 23 May 2017 в 12:01
поделиться

3 ответа

Создайте щелевой аллокатор:

Аллокатор создается с множеством страниц памяти, каждая одинакового размера (512k, 256k, размер должен быть настроен для вашего использования).

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

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

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

Edit: This answer is a bit long winded. Как обычно, boost прикроет вас.

6
ответ дан 7 December 2019 в 07:43
поделиться

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

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

1
ответ дан 7 December 2019 в 07:43
поделиться

Если вы знаете максимальный размер ваших массивов, вы можете использовать собственный распределитель. Вам придется написать класс распределителя самостоятельно. Он должен сразу выделить большой кусок памяти и преобразовать его в связанный список. Каждый раз, когда необходимо создать экземпляр объекта, вы удаляете хвост из списка. Каждый раз, когда объект нужно освободить, вы добавляете запись в список.

РЕДАКТИРОВАТЬ: вот образец из Бьярна Страуструпа Язык программирования C ++, 3-е издание :

class Pool
{
private:
  struct Link
    { Link * next; };

  struct Chunk
  {
    enum {size = 8*1024-16};

    Chunk * next;
    char mem[size];
  };

private:
  Chunk * chunks;
  const unsigned int esize;
  Link * head;

private:
  Pool (const Pool &) { }      // copy protection
  void operator = (Pool &) { } // copy protection

public:
  // sz is the size of elements
  Pool(unsigned int sz)
    : esize(sz < sizeof(Link*) ? sizeof(Link*) : sz),
      head(0), chunks(0)
    { }

  ~Pool()
  {
    Chunk * n = chunks;

    while(n)
    {
      Chunk * p = n;
      n = n->next;
      delete p;
    }
  }


public:

  // allocate one element
  void * alloc()
  {
    if(head == 0)
      grow();

    Link * p = head;
    head = p->next;

    return p;
  }

  // put an element back into the pool
  void free(void * b)
  {
    Link * p = static_cast<Link*>(b);
    p->next = head; //put b back as first element
    head = p;
  }

private:
  // make pool larger
  void grow()
  {
    Chunk* n = new Chunk;
    n->next = chunks;
    chunks = n;

    const int nelem = Chunk::size / esize;
    char * start = n->mem;
    char * last = &start [ (nelem - 1) * esize ];

    for(char * p = start; p < last; p += esize) // assume sizeof(Link) <= esize
      reinterpret_cast<Link>(p)->next = reinterpret_cast<Link *>(p + esize);

    reinterpret_cast<Link *>(last)->next = 0;
    head = reinterpret_cast<Link *>(start);
  }
};
0
ответ дан 7 December 2019 в 07:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: