Мне нужно хранить элементы (состояние структуры) в контейнере 2D-вектора, и каждый элемент должен быть уникальным.
Я храню такие элементы:
std::vector<std::vector<std::unique_ptr<State>>> m_container;
И у меня есть функция вставки
bool insert(State && value, std::size_t deepness);
Которая должна вставить «значение» в m_container [глубина], если значение уникально или новая глубина меньше, чем предыдущая, и в этом случае мне нужно удалить предыдущее состояние (вставляется return true).
У меня одновременно вставляются несколько потоков, у меня уже есть реализация, но я не уверен, что она лучшая, поэтому меня интересует лучший и более быстрый способ вставки или некоторые улучшения.
Моя реализация довольно длинная, поэтому я стараюсь уменьшить ее, сохраняя при этом логику.
кроме контейнера, у меня есть multimap:
std::multimap<std::size_t, std::pair<std::size_t, std::size_t>> m_multimap;
ключ: хеш состояния
пара_1: глубина
пара_2: положение государства в m_container [глубина]
У меня есть lockless_queue структуры Temp (lockless_queue m_queue), которую я вставляю сначала сюда, а затем в контейнер.
template<typename T>
class lockless_queue {
public:
// storeing the elements
struct node;
struct node_ptr;
// insert element
template<typename... Args>
void produce(Args&&... args);
void produce(T && data);
void produce(const T & data);
// consume all elements form queue
node_ptr consume_all();
// queue is not empty
operator bool() const;
};
struct Temp {
std::unique_ptr<State> value;
std::size_t hash;
std::size_t deepness;
bool exist = false;
Temp(State * v, std::size_t hash, std::size_t deepness, bool exist);
Temp(State && v, std::size_t deepness);
Temp move() {
return Temp(value.release(), hash, deepness, exist);
}
bool is_equal(const State & state) const;
bool is_equal(State * state) const;
bool is_equal(const Temp & other) const;
bool is_equal(Temp * other) const;
void swap(State && v, std::size_t d) {
deepness = d;
value.reset(new State(std::move(v)));
}
};
Следующая функция - это функция вставки, которая вставляется в locless_queue.
bool pre_emplace(State && value, std::size_t deepness) {
Temp temp(std::move(value), deepness);
const auto range = m_multimap.equal_range(temp.hash);
if (range.first == range.second) {
m_queue.produce(std::move(temp));
return true;
} else {
const auto & container = m_container;
const auto it = std::find_if (range.first, range.second, [&temp, &container](const auto & iter) {
return temp.is_equal(container[iter.second.first][iter.second.second].get());
});
if (it == range.second || deepness < it->second.first) {
temp.exist = true;
m_queue.produce(std::move(temp));
return true;
}
}
return false;
}
(в некоторых случаях возвращаемое значение является недопустимым, потому что очередь не уникальна, но это не проблема, я использую возвращаемое значение только для оценки количества выведенных элементов)
эта функция потребляет элементы из очереди во временную мультикарту хеша и Temp
void m_finalize_cycle(std::multimap<std::size_t, Temp> & multimap) {
auto head = m_queue.consume_all();
auto node = head.ptr;
while (node != nullptr) {
if (node->data.value == false) { node = node->next; continue; }
auto & temp = node->data;
const auto range = multimap.equal_range(temp.hash);
if (range.first == range.second) {
multimap.emplace(std::make_pair(temp.hash, temp.move()));
} else {
const auto it = std::find_if (range.first, range.second, [&temp](const auto & pair) {
return temp.is_equal(pair.second.value.get());
});
if (it == range.second) {
multimap.emplace_hint(range.first, std::make_pair(temp.hash, temp.move()));
} else if (temp.deepness < it->second.deepness) {
it->second.value.reset(temp.value.release());
it->second.deepness = temp.deepness;
}
}
node = node->next;
}
}
и эта функция загружает все значения Temp из предыдущего mulimep в m_container
void m_finalize_write(std::multimap<std::size_t, Temp> & multimap) {
for (auto &[hash, temp] : multimap) {
if (temp.exist) {
const auto range = m_multimap.equal_range(temp.hash);
auto & container = m_container;
const auto it = std::find_if (range.first, range.second, [&temp, &container](const auto & iter) {
return temp.is_equal(container[iter.second.first][iter.second.second].get());
});
if (it != range.second) {
m_container.at(it->second.first).at(it->second.second).reset(nullptr);
m_multimap.erase(it);
}
} else {
}
// extend m_container
extend_if_needed(temp.deepness);
m_container.at(temp.deepness).push_back(std::move(std::unique_ptr<State>(temp.value.release())));
m_multimap.emplace(hash, std::make_pair(temp.deepness, m_container[temp.deepness].size() - 1));
}
}
пока я вставляю элементы, я запускаю m_finalize_cycle каждые 10 микросекунд, а затем, если я закончил со вставками, я вызываю m_finalize_write.
Эта реализация мне подходит, но, к сожалению, это самая медленная часть моего кода, поэтому меня интересуют способы получше.
@Tas Спасибо за совет, я тоже опубликую там свой вопрос





Похоже, у вас есть рабочий код, и вы просто пытаетесь его оптимизировать или улучшить. Может лучше спросить на Code Review SE