LINUX.ORG.RU

Оптимизировать использование памяти

 , ,


0

3

Ок, новая головоломка всем адептам ооп, красивого кода и так же всем царями.

Дано: Класс который является адаптером для битовых масок больших размеров - 190 бит. Размер маски определён на этапе компиляции и не изменяется.

На данный момент класс является простой обёрткой над std::bitset (код приведён ниже)

Проблема заключается в данных которые там находятся.

Екземляры данного класса очень коротко живущие и их очень много. (каждые 10мс появляются и уничтожаются десятки тысяч). Количество использованной информации в большинствo из них довольно мало. Большая часть масок состоит из нулей и от одного до пяти активных битов. хотя переодически попадаются и по 30-40 битов. Очень редко могут появлятся почти все заженные биты.

Соотношение полезной информации к используемогу пространству очень и очень низкое.

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

Обязательные условия: 1. Нельзя пользоватся хипом 2. Должен остатся абсолютно тот же интерфейс класса. 3. Как можно более минимальный оверхед на обработку внутрених данных. 4. Нельзя использовать inline asm

Можно использовать любые техники -> от темплейтов до SIMD intrinsics.

Вроде ничего не забыл. Если появятся вопросы - я отвечу.

Ну и сам код класса;

#include <inttypes.h>
#include <assert.h>
#include <bitset>

#define CHANGESET_MAX_BITS 190

class ChangeSet
{
public:
  inline ChangeSet()
  {}

  inline explicit ChangeSet(uint32_t uniqueChangeID)
  {
	//The current implementation of ChangeSet is limited to total 190 changes
    assert(uniqueChangeID < CHANGESET_MAX_BITS);
    m_changes.set(uniqueChangeID, 1);
  }

  inline ChangeSet(const ChangeSet & other): m_changes(other.m_changes)
  {}

  inline void clear()
  {
    m_changes.reset();
  }

  inline bool empty() const
  {
    return m_changes.none();
  }

  inline ChangeSet & operator |=(const ChangeSet & other)
  {
    m_changes |= other.m_changes;
    return *this;
  }

  inline ChangeSet operator |(const ChangeSet & other) const
  {
    ChangeSet out(*this);
    out |= other;
    return out;
  }

  inline void addChanges(const ChangeSet & other)
  {
    m_changes |= other.m_changes;
  }

  inline void removeChanges(const ChangeSet & other)
  {
    m_changes &= ~other.m_changes;
  }

  inline ChangeSet & operator &=(const ChangeSet & other)
  {
    m_changes &= other.m_changes;
    return *this;
  }

  inline ChangeSet operator &(const ChangeSet & other) const
  {
    ChangeSet out(*this);
    out &= other;
    return out;
  }

  inline ChangeSet & operator ^=(const ChangeSet & other)
  {
    m_changes ^= other.m_changes;
    return *this;
  }

  inline ChangeSet operator ^(const ChangeSet & other) const
  {
    ChangeSet out(*this);
    out ^= other;
    return out;
  }

  inline ChangeSet operator ~() const
  {
    ChangeSet out;
    out.m_changes = ~m_changes;
    return out;
  }

  inline operator bool() const
  {
    return !m_changes.none();
  }

  inline bool containsAnyOf(const ChangeSet & other) const
  {
    return !(m_changes &  other.m_changes).none();
  }

  inline bool operator ==(const ChangeSet & other) const
  {
    return m_changes == other.m_changes;
  }

  inline bool operator !=(const ChangeSet & other) const
  {
    return m_changes != other.m_changes;
  }

  static inline ChangeSet generateChangeSetWithAllChangesOn()
  {
    ChangeSet out;
    out.m_changes.flip();
    return out;
  }

private:
  std::bitset<CHANGESET_MAX_BITS> m_changes;
};


Кроме VLQ + alloca ничего в голову не приходит, но и здесь нужно будет повозиться.

mix_mix ★★★★★
()

Оптимизировать использование памяти
Нельзя пользоватся хипом

/0

Если под заголовком понимается оптимизация вычислений - то ок, но память тут ни каким боком.

любые техники -> от темплейтов

Каким образом шаблоны влияют на производительность?

Кто мешает написать свою версию bitset на simd? stdc++ по умолчанию медленный.

А если серьезно,то:

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

вот это и нужно оптимизировать, то есть логику, а не заниматься микрооптимизациями.

anonymous
()

Напиши свой bitset с блэйджэком и шлюхамисжатием (RLE например) и alloca. Или возьми готовую имплементацию, того что тебе надо.

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)

каждые 10мс появляются и уничтожаются десятки тысяч
1. Нельзя пользоватся хипом

stack overflow тебя ждет. Что за дурацкое требование?

invy ★★★★★
()

Ох,ну можно 190 бит побить на группы по 6 бит. У нас получается 32 группы (одна неполная). Заводим 32 битное число и массив бит. Каждый бит числа указывает, есть ли в данной группе хотя бы один бит. Если нет, в массиве бит эта группа полностью отсутствует. Если повезет, то на этом можно сохранить какие-нибудь биты.

Можно попытаться хранить все данные в разреженном битовом дереве. Идея та же, только вместо 32-бит маски дерево.

Предположим мы хотим закодировать число 10001000, структура данных будет иметь вид 1110101010

Начинаем с верха бинарного дерева, если у ветви «1», то у её подветвей есть биты с «1», если нет, то эти подветви не приводятся.

Вот дерево

    1/  \1   --> 11
  1/\0 1/\0  --> 10 10 --> 1110101010
1/\0  1/\0   --> 10 10

Тут экономии в битах нет, но на больших, сильноразреженных данных она должна быть

А то, что хипом нельзя пользоваться, это печать. Может какой-нибудь mmap/unmap? Но все равно ТС поставил весьма извращенную задачу.

pathfinder ★★★★
()

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

Обязательные условия: 1. Нельзя пользоватся хипом

А тебе не кажется, что это взаимоисключающие требования? Как ты представляешь себе экономию памяти на стеке если размер объекта определяется его состоянием (количеством бит) и неизвестен во время компиляции? С другой стороны чем может не устраивать std::bitset постоянного размера?

#define CHANGESET_MAX_BITS 190

В Си++98 для этого есть нормальные константы, в C++11 - ещё и constexpr.

asaw ★★★★★
()

Если ты не пользуешься хипом, и создаешь свои объекты на стеке, зачем тебе вообще оптимизация по памяти на эти операции, если размер аллокации под стек все равно не изменится?

CatsCantFly
()
Ответ на: комментарий от CatsCantFly

Кто то снаружи может сделать допустим std::vector<ChangeSet>, но сам он не должен делать дополнительные алокации

CyberK
() автор топика
Ответ на: комментарий от CyberK

Ты можешь уменьшить количество информации для одной маски в среднем, но в худшем случае всё равно будет 190 бит. Следовательно, либо экземляр класса должен быть переменного размера, либо содержать указатель в пул/кучу. Первое реализуемо только хаком с alloca и никакого std::vector<ChangeSet> ты здесь уже не получишь, второе ты отметаешь в условии. От чего-то придётся отказаться.

mix_mix ★★★★★
()

новая головоломка

Это не головоломка, а плохо сформулированная задача. У тебя прямо в условии есть взаимно противоречащие требования, причём жёсткие, без возможности размена. Тебе это даже доказали.

У хорошей головоломки должны быть краткая и чёткая формулировка и простое, но неожиданное решение. Пример хорошей головоломки: http://m.c.lnkd.licdn.com/mpr/mpr/p/1/005/089/016/37a00f9.jpg

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

ну сходу очевидное решение: s/i--/n--/

А остальные 2 пока не увидел.

DELIRIUM ☆☆☆☆☆
()
Ответ на: комментарий от i-rinat

пост тщеславия

Да ладно, все три решения довольно очевидные, ничего неожиданного, увы, мне 7-8 минут хватило.

mix_mix ★★★★★
()
Ответ на: комментарий от i-rinat

Пример хорошей головоломки

Ну для нулей - да, она что-то ломает.

Норм пацанам она ничего не даёт - раз, а решение её абсолютно бесполезно. Зачем она нужна?

anonymous
()

Еще вариант. Заводишь пул для выделения памяти в виде глобальной переменной и пишешь свой аллокатор. Но опять же у тебя будет строго ограниченный объем памяти и. vector<ChangeSet> в общем случае будет делать нельзя.

invy ★★★★★
()

190 это на ~1% меньше чем 192 что 64*3

на современных CPU машиные типы обычно 8, 16,32,64,128,256 битные если есть.

для максимального использования simd или вообще хранишь 190 битные данные в 256 битных ( ну да четверт памяти лесом :( ) либо хранишь квадру битных в тройке 256 - а точнее хекстет в дюжине а ещё лучше

т.е пакуешь 4 190 битные числа как 192 битные в 3 256битных и вперёд. -

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

и да как всяких фанат правильного фортрана - никакой динамики вся память заранее распределена обёрткой :)

qulinxao ★★☆
()
Ответ на: комментарий от invy

vector<ChangeSet> в общем случае будет делать нельзя.

Можно специализировать std::allocator<ChangeSet>. Vector его заюзает.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

http://m.c.lnkd.licdn.com/mpr/mpr/p/1/005/089/016/37a00f9.jpg

Можно убрать ИЛИ добавить только один символ? В перед i в i < n поставить минус.

Если заменить только один символ, то сходу ещё два решения придумать можно. Вместо i-- поставить n--. И в условии вместо знака < поставить +.

a1batross ★★★★★
()
Ответ на: комментарий от a1batross

будь печать например | можно было бы показуистить s/-/+/g как замену одного символа :)

qulinxao ★★☆
()
Последнее исправление: qulinxao (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.