map insert comparison
Question> I try to understand the usage of insert & emplace with hint introduced to std::map. During the following test, it seems to me that the old fashion insert is fastest.
Did I do something wrong here?
Thank you
static void MapEmplaceWithHint(benchmark::State& state) {
std::vector<int> v{12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
std::map<int, int> mapInt;
auto where(std::end(mapInt));
for (auto _ : state) {
for (const auto &n : v) { // Items in non-incremental order
where = mapInt.emplace_hint(where, n, n+1);
}
}
}
BENCHMARK(MapEmplaceWithHint);
static void MapInsertWithHint(benchmark::State& state) {
std::vector<int> v{12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
std::map<int, int> mapInt;
auto where(std::end(mapInt));
for (auto _ : state) {
for (const auto &n : v) { // Items in non-incremental order
where = mapInt.insert(where, {n, n+1});
}
}
}
BENCHMARK(MapInsertWithHint);
static void MapInsertNoHint(benchmark::State& state) {
std::vector<int> v{12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
std::map<int, int> mapInt;
for (auto _ : state) {
for (const auto &n : v) { // Items in non-incremental order
mapInt.insert({n, n+1});
}
}
}
BENCHMARK(MapInsertNoHint);
static void MapReverseInsertNoHint(benchmark::State& state) {
std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12};
std::map<int, int> mapInt;
for (auto _ : state) {
for (const auto &n : v) { // Items in incremental order
mapInt.insert({n, n+1});
}
}
}
BENCHMARK(MapReverseInsertNoHint);
static void MapEmplaceNoHint(benchmark::State& state) {
std::vector<int> v{12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
std::map<int, int> mapInt;
for (auto _ : state) {
for (const auto &n : v) { // Items in non-incremental order
mapInt.emplace(n, n+1);
}
}
}
BENCHMARK(MapEmplaceNoHint);
First, let's create a dataset more meaningful than 12 integers:
std::vector<int> v(10000);
std::iota(v.rbegin(), v.rend(), 0);
Results from all functions are now more comparable: https://quick-bench.com/q/HW3eYL1RaFMCJvDdGLBJwEbDLdg
However, there's a worse thing. Notice that looping over state makes it perform the same operations several times to measure the average time. But, since you are reusing the same map, each insert or emplace after the first loop iteration is failing, so you mostly measure time of failed inserts, where hint doesn't help.
Test cases should look more like this:
std::vector<int> v(1000);
std::iota(v.rbegin(), v.rend(), 0);
for (auto _ : state) {
std::map<int, int> mapInt;
auto where(std::end(mapInt));
for (const auto &n : v) { // Items in non-incremental order
where = mapInt.emplace_hint(where, n, n+1);
}
}
And with this, hints start to shine (had to limit data to 1000, otherwise I'd get timeouts): https://quick-bench.com/q/2cR4zU_FZ5HQ6owPj9Ka_y9FtZE
I'm not sure if the benchmarks are correct, but quick glance in the assembly suggests that inserts were not optimized altogether, so there's a chance it's good enough.
As noticed by Ted Lyngmo, try_emplace() with hint tends to perform (slightly) better:
https://quick-bench.com/q/evwcw4ovP20qJzfsyl6M-_37HzI
Related
I want to write a function like this:
template<class IterableType>
void CheckAndProcessIterables(IterableType& a, IterableType& b, IterableType& c) {
IteratorRangeType range{}; // empty range
if (Check(a)) {
range = boost::range::join(range, a);
}
if (Check(b)) {
range = boost::range::join(range, b);
}
if (Check(c)) {
range = boost::range::join(range, c);
}
Process(range);
}
Is it possible? Which type should I use instead of IteratorRangeType?
As far as I understand it, boost::range::join return type depends on it's arguments. Is there some wrapper class which can be assigned any type of range as long as its underlying value type is same?
You can use type erased iterator ranges, which Boost has in the form of any_range.
Beware of the performance cost of these, which can quickly become very noticable. I'd rethink the approach unless you're very sure that this not on any hot path and readability is a much more of a concern than performance.
Live On CoCompiler Explorer
#include <boost/range/join.hpp>
#include <boost/range/any_range.hpp>
// for demo only:
#include <boost/range/algorithm/for_each.hpp>
#include <boost/lambda/lambda.hpp>
#include <fmt/ranges.h>
template<class Range>
bool Check(Range const& r) {
bool odd_len = boost::size(r) % 2;
fmt::print("Check {}, odd_len? {}\n", r, odd_len);
return odd_len;
}
template<class Range>
void Process(Range const& r) {
fmt::print("Processing {}\n", r);
using namespace boost::lambda;
for_each(r, _1 *= _1);
}
template<class IterableType>
void CheckAndProcessIterables(IterableType& a, IterableType& b, IterableType& c) {
using V = typename boost::range_value<IterableType>::type;
using ErasedRange= boost::any_range<V, boost::forward_traversal_tag>;
ErasedRange range{}; // empty range
if (Check(a)) {
range = boost::range::join(range, a);
}
if (Check(b)) {
range = boost::range::join(range, b);
}
if (Check(c)) {
range = boost::range::join(range, c);
}
Process(range);
}
int main() {
std::vector a{1, 2, 3}, b{4, 5}, c{6, 7, 8};
CheckAndProcessIterables(a, b, c);
fmt::print("After process: a:{} b:{} c:{}\n", a, b, c);
}
Prints
Check {1, 2, 3}, odd_len? true
Check {4, 5}, odd_len? false
Check {6, 7, 8}, odd_len? true
Processing {1, 2, 3, 6, 7, 8}
After process: a:{1, 4, 9} b:{4, 5} c:{36, 49, 64}
My problem is : i gave a series of queries, and a series of references, and i want to count the number of occurance of said keys between them, but only if they have matching keys. I choose to have an LUT because i think it will help me efficiently, but im not sure if there are better ways, or the way im using LUT is not efficient enough.
i have the following data structures.
unordered_map <int, set<int>> Reference_map1, Reference_map2, ... , Reference_mapM;
// to story all the reference maps by their neightas
unordered_map <string, unordered_map <int, set<int>>> ReferenceMaps;
unordered_map <int, set<string>> LUT // look up table
// N is significantly greater than M
unordered_map <int, set<int>> query_map1, query_map2, ... , query_mapN;
Example of Reference_mapi and query_mapj
Reference_map1[111] = {0, 1, 2};
Reference_map1[333] = {1, 2, 3};
Reference_map1[888] = {2, 8, 0};
Reference_map2[111] = {1, 5, 9};
Reference_map2[999] = {0, 7, 4};
ReferenceMaps['Reference_map1']=Reference_map1;
ReferenceMaps['Reference_map2']=Reference_map2;
query_map1[111] = {8, 2, 6};
query_map1[333] = {4, 7, 3};
query_map2[222] = {3, 6, 8};
query_map2[999] = {2, 3, 5};
How i store my look up table LUT
This is so that for whatever keys i get from 'query_mapj', i only get the necessary Reference_mapis
LUT[111] = {'Reference_map1', 'Reference_map2'}
LUT[333] = {'Reference_map1'}
LUT[888] = {'Reference_map1'}
LUT[999] = {'Reference_map2'}
For example 111 from query_map1 gives both 'Reference_map1', 'Reference_map2' as they have the key 111.
On the other hand, 999 from query_map2 only gives 'Reference_map2' as only it have the key 999.
So it will go like this:
unordered_map<string, int> MakeCounter(
unordered_map <int, set<int>> &query_map,
unordered_map <string, unordered_map <int, set<int>>> &ReferenceMaps
){
unordered_map<string, int> RefName_Counter;
set<string> ReferenceNameSet;
// Update the RefName_Counter
for (const auto &key2nameset:query_map) {
// Check if this hash is in the LUT
if (LUT.count(key2nameset.first) <= 0){ continue; }
// Update Counter
ReferenceNameSet = LUT[key2nameset.first];
for (const auto &it : ReferenceNameSet){
if (RefName_Counter.count(it) > 0)
RefName_Counter[it]++;
else
RefName_Counter[it] = 1;
}
}
return RefName_Counter;
}
// The results should be like this
Counter1 = MakeCounter(query_map1, ReferenceMaps);
/*
Counter1['Reference_map1'] = 2; // because they share keys : 111 and 333
Counter1['Reference_map2'] = 1; // because they share keys : 111
*/
Counter2 = MakeCounter(query_map2, ReferenceMaps);
/*
Counter1['Reference_map2'] = 1; // because they share keys : 999
*/
Is there a better way to get Counteri for each respective query_mapi ?
Considering my comment above, here is what I got.
Adjusted to keep original data, but this feels like an extra credit :)
unordered_map<int,int> MakeCounter(const unordered_map <int, set<int>>& qs,
const vector<unordered_map <int, set<int>>>& refs)
{
unordered_map<int, int> counters;
for (size_t i = 0; i < refs.size(); ++i)
{
for (auto q : qs)
{
if (refs[i].find(q.first) != refs[i].end())
counters[i]++;
}
}
return counters;
}
int main()
{
vector<unordered_map <int, set<int>>> References = {
{ {111, { 0, 1, 2 } },
{333, { 1, 2, 3 } },
{888, { 2, 8, 0 } },
},
{ {111, { 1, 5, 9 } },
{999, { 0, 7, 4 } },
}
};
vector<unordered_map <int, set<int>>> Queries = {
{ {111, { 8, 2, 6 } },
{333, { 4, 7, 3 } },
},
{ {222, { 3, 6, 8 } },
{999, { 2, 3, 5 } },
}
};
unordered_map<int, int> m0 = MakeCounter(Queries[0], References);
unordered_map<int, int> m1 = MakeCounter(Queries[1], References);
}
This is rather a comment; I am using "answer" to properly format code fragments.
This searches your LUT twice:
// Check if this hash is in the LUT
if (LUT.count(key2nameset.first) <= 0){ continue; }
// Update Counter
ReferenceNameSet = LUT[key2nameset.first];
It also makes a copy of the set for no reason.
I believe that indexing a non-existing element in the map inserts that element, so
if (RefName_Counter.count(it) > 0)
RefName_Counter[it]++;
else
RefName_Counter[it] = 1;
is effectively:
RefName_Counter[it]++;
So your outer for loop becomes:
for (const auto &key2nameset:query_map) {
// Check if this hash is in the LUT
auto iter = LUT.find(key2nameset.first);
if(iter == LUT.end()) { continue; }
// Update Counter
for (const auto &it : *iter){
RefName_Counter[it]++;
}
I made a function that merges two sorted queues.
Queue<int> merge(Queue<int> a, Queue<int> b){
Queue<int> result;
while (!a.isEmpty() && !b.isEmpty()) {
int a1 = a.peek();
int b1 = b.peek();
if (a1 < b1) {
if (! result.isEmpty()) {
if (result.back() > a1) {
error("queue a is not sorted");
}
}
result.add(a1); // add the element to the result and make sure to remove it from the
a.dequeue(); // input queue so we don't get stuck in an infinite loop
} else {
if (! result.isEmpty()) {
if (result.back() > b1) {
error("queue b is not sorted");
}
}
result.add(b1);
b.dequeue();
}
} while (!a.isEmpty()) {
if (! result.isEmpty()) {
if (result.back() > a.peek()) {
error("queue a is not sorted");
}
}
result.add(a.front());
a.dequeue();
} while (!b.isEmpty()) {
if (! result.isEmpty()) {
if (result.back() > b.peek()) {
error("queue b is not sorted");
}
}
result.add(b.front());
b.dequeue();
}
return result;}
Now, I am trying to merge multiple queues together, recursively. Here is my thought process so far:
Divide the input collection of k sequences into two halves, left and right.
Make a recursive call to recMultiMerge on the "left" half of the sequences to generate one combined, sorted sequence. Then, do the same for the "right" half of the sequences, generating a second combined, sorted sequence.
Using the binary merge function I made above, join the two combined sequences into the final result sequence, which is then returned.
I'm having trouble on the actual recursive call, because I can't figure out how to store the result and recurse again. Here is my attempt so far:
Queue<int> recMultiMerge(Vector<Queue<int>>& all)
{
Queue<int> result = {};
Vector<Queue<int>> left = all.subList(0, all.size() / 2);
Vector<Queue<int>> right = all.subList(all.size() / 2, all.size() / 2);
if (all.isEmpty()) {
return {};
}
else if (left.size() == 1) {
return left[0];
}
else if (right.size() == 1) {
return right[0];
}
else {
Queue<int> leftCombined = recMultiMerge(left);
Queue<int> rightCombined = recMultiMerge(right);
result = merge(leftCombined, rightCombined);
}
return result;
}
The problem is, I can't get it to return more than just the first queue. Here is the problem illustrated in a test case:
on
Vector<Queue<int>> all = {{3, 6, 9, 9, 100}, {1, 5, 9, 9, 12}, {5}, {}, {-5, -5}, {3402}}
it yields
{3, 6, 9, 9, 100}
instead of
{-5, -5, 1, 3, 5, 5, 6, 9, 9, 9, 9, 12, 100, 3402}
Any advice?
An explanation of why your code gives the results you see.
The first call to recMultiMerge has 6 queues. left will be the first three ({3, 6, 9, 9, 100}, {1, 5, 9, 9, 12}, {5}), and right will be the last three ({}, {-5, -5}, {3402}).
Then you make a recursive call with left. In that call, all.size() will be 3. left will have one queue ({3, 6, 9, 9, 100}), and right will also only have one queue ({1, 5, 9, 9, 12}). (I'm assuming the 2nd parameter to Vector.subList is a count.) This will stop at the second if because left.size() == 1. The result will be that first queue.
Now we're back at the first recursive call (having lost the 2nd and 3rd queues), and we again main a recursive call with right (which has 3 queues in it). This will proceed like the last call did, returning the first queue (which in this case is empty) and losing the other two.
Then you merge those two queues ({3, 6, 9, 9, 100} and {}), resulting in your answer: {3, 6, 9, 9, 100}.
This reveals two problems: Not properly dividing a Vector with an odd number of queues in it, and terminating the recursion too early (when the left half of the split only has one queue in it, even though the right half may not be empty).
I'd start with a binary fold.
template<class X, class Op>
X binary_fold( span<X> elems, Op op );
or std::vector<X>, but I prefer span for this.
It splits elems into two pieces, then either recurses or calls op on it.
You can then test binary_fold with debugging code that simply prints the pieces on the left/right in some way, and you can see how the recursion plays out.
Once you have that, you plug back in your merge program and it should just work.
Live example.
Full code:
template<class X>
struct span {
X* b = 0;
X* e = 0;
X* begin() const { return b; }
X* end() const { return e; }
std::size_t size() const { return end()-begin(); }
X& front() const { return *begin(); }
X& back() const { return *(end()-1); }
X* data() const { return begin(); }
bool empty() const { return size()==0; }
span( X* s, X* f ):b(s),e(f) {}
span() = default;
span( X* s, std::size_t l ):span(s, s+l) {}
span( std::vector<X>& v ):span( v.data(), v.size() ) {}
template<std::size_t N>
span( X(&arr)[N] ):span(arr, N) {}
template<std::size_t N>
span( std::array<X, N>& arr ):span(arr.data(), N) {}
span except_front( std::size_t n = 1 ) const {
n = (std::min)(n, size());
return {begin()+n, end()};
}
span only_front( std::size_t n = 1 ) const {
n = (std::min)(n, size());
return {begin(), begin()+n};
}
span except_back( std::size_t n = 1 ) const {
n = (std::min)(n, size());
return {begin(), end()-n};
}
span only_back( std::size_t n = 1 ) const {
n = (std::min)(n, size());
return {end()-n, end()};
}
};
template<class X, class Op>
X binary_fold( span<X> elems, Op op ) {
if (elems.empty()) return {};
if (elems.size() == 1) return elems.front();
auto lhs = binary_fold( elems.only_front( elems.size()/2 ), op );
auto rhs = binary_fold( elems.except_front( elems.size()/2 ), op );
return op(std::move(lhs), std::move(rhs));
}
I am trying to initialize an std::unordered_map using the constructor that accepts data through an initialization list and the initial number of buckets.
For some reason, that constructor works if I put it in main, but has a syntax error when I put it in a class header.
Specifically, the header, called momo.h:
#pragma once
#include <unordered_map>
namespace std
{
template <>
struct hash<std::pair<uint16_t, uint16_t>>
{
std::size_t operator()(const std::pair<uint16_t, uint16_t>& k) const
{
return (std::hash<long>()((((long)k.first) << 16) + (long)k.second));
}
};
}
class test
{
std::unordered_map<std::pair<uint16_t, uint16_t>, uint16_t> m_Z(
{ /* Fails here: syntax error: missing ')' before '{' */
{std::pair{ 1, 2 }, 3},
{std::pair{ 4, 5 }, 6}
}, 128);
};
While if I remove the definition from the header into main thus:
#include "Momo.h"
int main()
{
test X;
std::unordered_map<std::pair<uint16_t, uint16_t>, uint16_t> Y(
{
{std::pair{ 1, 2 }, 3},
{std::pair{ 4, 5 }, 6}
}, 128);
}
The code compiles without error. Why?
You need to braced-init-list(or uniform-initiation) the std::unordered_map in the class.
class test
{
std::unordered_map<std::pair<uint16_t, uint16_t>, uint16_t> m_Z{ // >> brased init
{
{std::pair{ 1, 2 }, 3},
{std::pair{ 4, 5 }, 6}
}, 128
}; // >>>
};
I am attempting to determine at compile time if all the values in a std::initializer_list are unique. I was able to locate a solution to valiate the size of a list but have been unable to apply it to the contents. I have tried with both free functions and in constructors but both approaches have resulted in the following errors with GCC 4.7.2.
error: non-constant condition for static assertion
error: 'begin' is not a constant expression
I realize the members of std::initializer_list are not declared constexpr but I'm hoping there is a solution like the size validation. Is it possible to validate the contents at compile time using something like the following?
#include <initializer_list>
template<typename InputIterator>
constexpr bool Validate(InputIterator begin, InputIterator end)
{
static_assert(*begin == *end, "begin and end are the same");
// The actual implemetnation is a single line recursive check.
return true;
}
template<typename InputType>
constexpr bool Validate(const std::initializer_list<InputType>& input)
{
// "-1" removed to simplify and eliminate potential cause of error
return Validate(input.begin(), input.end() /* - 1 */);
}
int main()
{
Validate({1, 2, 1});
}
After some digging it looks like using std::initializer_list is not possible in GCC 4.7 due to the lack of constexpr in it's declaration. It should work with GCC 4.8 as <initializer_list> has been updated to include constexpr. Unfortunately using GCC 4.8 is not an option at the moment.
It is possible to access elements of an array if the decayed pointer is passed by reference though. This allows the validation to occur as desired but still isn't quite the solution I am hoping for. The following code is a workable solution for arrays. It still requires that the size of the array be supplied to the validation function but that it easy enough to correct.
#include <initializer_list>
template<typename T>
constexpr bool Compare(T& data, int size, int needleIndex, int haystackIndex)
{
return
needleIndex == haystackIndex ?
Compare(data, size, needleIndex + 1, haystackIndex)
: needleIndex == size ?
false
: data[needleIndex] == data[haystackIndex] ?
true
: Compare(data, size, needleIndex + 1, haystackIndex);
}
template<typename T>
constexpr bool Compare(T& data, int size, int index)
{
return
index == size ?
false
: Compare(data, size, index + 1) ?
true
: Compare(data, size, 0, index);
}
template<typename T, int ArraySize>
constexpr bool Validate(T(&input)[ArraySize], int size)
{
return !Compare(input, size, 0);
}
int main()
{
constexpr int initData0[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
constexpr int initData1[] = {1, 1, 2, 3, 4, 5, 6, 7, 8, 9};
constexpr int initData2[] = {2, 1, 2, 3, 4, 5, 6, 7, 8, 9};
constexpr int initData3[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 8};
constexpr int initData4[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 7};
constexpr int initData5[] = {0, 1, 0, 3, 4, 5, 6, 7, 8, 9};
constexpr int initData6[] = {0, 1, 2, 3, 4, 5, 6, 9, 8, 9};
static_assert(Validate(initData0, 10), "set 0 failed"); // <-- PASS
static_assert(Validate(initData1, 10), "set 1 failed"); // <-- (and below) FAIL
static_assert(Validate(initData2, 10), "set 2 failed");
static_assert(Validate(initData3, 10), "set 3 failed");
static_assert(Validate(initData4, 10), "set 4 failed");
static_assert(Validate(initData5, 10), "set 5 failed");
static_assert(Validate(initData6, 10), "set 6 failed");
}
.
Build log:
C:\Source\SwitchCaseString\main.cpp: In function 'int main()':
C:\Source\SwitchCaseString\main.cpp:198:2: error: static assertion failed: set 1 failed
C:\Source\SwitchCaseString\main.cpp:199:2: error: static assertion failed: set 2 failed
C:\Source\SwitchCaseString\main.cpp:200:2: error: static assertion failed: set 3 failed
C:\Source\SwitchCaseString\main.cpp:201:2: error: static assertion failed: set 4 failed
C:\Source\SwitchCaseString\main.cpp:202:2: error: static assertion failed: set 5 failed
C:\Source\SwitchCaseString\main.cpp:203:2: error: static assertion failed: set 6 failed