GithubHelp home page GithubHelp logo

tessil / robin-map Goto Github PK

View Code? Open in Web Editor NEW
1.2K 1.2K 108.0 862 KB

C++ implementation of a fast hash map and hash set using robin hood hashing

License: MIT License

CMake 2.50% C++ 97.50%
c-plus-plus cpp data-structures hash-map hash-table header-only

robin-map's People

Contributors

2aecfff4 avatar adrianatgoogle avatar cmakshin avatar milianw avatar stainleebakhla avatar tessil avatar wjakob avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

robin-map's Issues

Adding ints with poor hash leads to explosive reserve sequence in some cases

I run the following simple program. I do believe on libstdc++, wrapper could be replaced just with unsigned long long but I tested with VC++ so I had to write it.

#include "tsl/robin_set.h"

class wrapper
{
public:
  bool operator== (const wrapper &other) const { return x == other.x;}
  unsigned long long x;
};

namespace std
{
  template <>
  struct hash<wrapper>
  {
    size_t operator()(const wrapper &w) const noexcept { return w.x; }
  };
}

int main ()
{
  tsl::robin_set<wrapper> s;
  auto fp = fopen ("a.txt", "r");
  size_t x;
  while (fscanf (fp, "%llu", &x) == 1)
    {
      s.insert (wrapper {x});
      std::cout << s.bucket_count () << '\n';
    }
  fclose (fp);
}

Basically I add numbers from attached a.txt to robin_set

Print results at the end of execution are akin to the following:

8192
8192
8192
16384
16384
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912

With program taking 6-8 GB of RAM in the end. I believe adding a single number to the end of the file results in one more 2x reserved memory increase.
We could observe this problem only after recent update so it probably has to do with some recent changes in robin_map. While I understand that this hash is probably poor it is still default in libstdc++ and it would be nice if such behavior would be fixed.

Edit:
Bisect shows that 73959d6 is where it started to happen

Conan Package Support

Hello,

Do you know about Conan?

Conan is modern dependency and package manager for C/C++ and would be great if your library would be available via package manager for other developers.

All Conan packages are available on Conan Center, where is possible search, install and upload any official Conan package.

As your project is header-only, people will not need to download manually each new release. Other header-only projects, as Catch2, are available on Conan Center.

We could support you writing a recipe and publishing your packages on Bintray.

If you have any questions, just ask :-)

Regards!

A C2028 error may be generated when using the c++ 20 module.

OS: Windows 10
IDE: vs 2022 17.3.6

ixx:

module;
#include "tsl/robin_map.h"
export module Test;
export namespace Test {
    inline tsl::robin_map<int, int> m = {};
}

cpp:

module Test;
namespace Test{
void Test1(){
    // tsl\robin_hash.h(385,25): error C2028
    auto sz = m.size();
}
}

robin_map and Poco

Hi,
After updating my Poco library (https://github.com/pocoproject) from version 1.9.0. to 1.9.4. I got errors mi my project for tsl:robin_map.h. I was able to fix them by adding:
#undef min in robin_hash.h
and
#undef max in robin_growth_policy.h

I guess something changed in Poco, which was defining the min and max.

I am not sure if you need to change that in the tsl:robim-map project, I am just let you know about that behaviour :D

P.S. I love your robin-map 😄

map entry goes missing after a large number of insertions

I have a map that is <int, void *> and a previously inserted entry goes 'missing' after a large number of insertions of other key/value pairs. The key value is 1597736, and it goes missing from the map after inserting a key of value 9861762. The size of the map is 1251645 when the problem occurs.

This happens on Linux , and not Windows oddly. I have a reproducer.

keys.txt.gz

test.zip

I am using GCC 6.3.1 on Centos 7 and compiling with
c++ test.cpp -o test

Maybe it would make sense for the self-insertion/emplacement to work (suggestion)

Currently the following code leads to crash/undefined behavior with tsl::robin_map:

int main ()
{
  tsl::robin_map<int, std::string> v;
  v[0] = "23";
  for (int i = 2; i < 3300; ++i)
    v[i] = v[0];
}

It happens because we pass the reference to our own element which gets destroyed during reallocation and since construction happens late it leads to the unfortunate results. I understand that this might be a design decision to treat it as UB however I have the following reason for suggesting to fix this:

  1. It works fine with node-based (std::) maps.

  2. It works with std::vector. I can't quote standard paragraph exactly but here's link to the statement from Microsoft of fixing this problem in their implementation https://devblogs.microsoft.com/cppblog/stl-fixes-in-vs-2017-rtm/ (Fixed aliasing bugs)

  3. It actually seems to be much more frequent operation for unordered map rather than for vector, simply copying a past value to the newly created field feels like natural operation in many cases.

  4. A fix seems to be the same as for vector since it is the structure robin map built upon so should not be that hard to implement.

CL.exe crash

When built with MSVC and c++20 modules, CL.exe crashes with code -1073741571.

This is possibly caused by a recursive concept, as suggested here.

No matching function swap() when using robin_map::emplace()

Hello, @Tessil,

I might be missing something in the documentation but I figured I would ping you on this. Basing my work on the few differences between the robin_map and the unordered_map, I don't understand why the following code does not compile:

#include <unordered_map>
#include "external/tsl/robin_map.h"

struct SomeStruct {
  int val_;

  SomeStruct() : val_(0) {  }
  SomeStruct(int val) : val_(val) {  }
  SomeStruct& operator=(const SomeStruct&) = delete; // Comment this out and it compiles
};

int main(int argc, char *argv[]) {
  std::unordered_map<int, SomeStruct> unordered_map;
  unordered_map.emplace(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple());

  tsl::robin_map<int, SomeStruct> robin_map;
  robin_map.emplace(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple());
}

I get the following error:

error: no matching function for call to ‘swap(SomeStruct&, SomeStruct&)’
  swap(second, __p.second);

Keeping the assignment operator by not explicitly deleting it gets rid of the issue but I want to have it disabled. Any idea why emplace would need it in the robin_map?

(Also, I am using C++ 11)

Thanks in advance! :)

pointer to value and hashmap resize

Hi, I wonder if it is valid to store a pointer to mapped value. When will this pointer be invalidated? Does map resize trigger an invalidation? Thanks.

Compiling with "-Wshadow" flag causes error

Hi,

Compiling this with -Wshadow flag causes it to error out. If I set the -Wno-shadow flag, compilation passes successfully.

Currently I'm trying it to include it in a project of mine, but I'm not allowed to change any compilation flags. Would be really helpful if this problem could be solved.

Thanks
Stainlee

while using the robin_pg_map, there was an error

Hi, there was an error(SIGABORT) using robin_pg_map in my code . below was the cause.

prime_growth_policy
/usr/local/include/tsl/robin_growth_policy.h:283

robin_growth_policy.h:283 => TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maxmimum size.");

The number of PRIMES in the prime_growth_policy is 40, if I add the number of primes, does the error resolve?

Rehash "down" support on erase

Hello.

Are there any plans to support hashtable "shrink" on erases and reaching sufficiently low load_factor (like maybe 0.125) ?

Updated Benchmark?

Hi,
Your benchmark is great and very extensive. Could you run it again now so to see how all libs have improved over the past 4 or so years? Also some have stopped development and maybe some hot new ones appeared? Would be so awesome!

robin_map<int, struct *>, build failed

struct TaskInfo
{
uint32_t unMessageID_;
};
tsl::robin_map<std::int64_t, struct TaskInfo *> test(20);

it.value()->unMessageID_ = -1;

Hi,
when i build , the error is
‘using value_type = const value_type’ has no member named ‘value’.
I try to find this, but i don't know how to do fix it. may i loss some operation?

how to write range based for

For iterators of tsl::robin_map, operator*() and operator->() return a reference and a pointer to const std::pair<Key, T> instead of std::pair<const Key, T> making the value T not modifiable. To modify the value you have to call the value() method of the iterator to get a mutable reference.

This is very convenient, all user code has to change to adopt this.
Besides is there a way to write ranged based for which is recommended for modern c++?
tsl::robin_map<int, int> map = {{1, 1}, {2, 1}, {3, 1}};
for(auto& it : map) {
it.second = 2; // Illegal, any workaround??
}

Is it a implementation or design choice for robin-map to use const std::pair<Key, T> instead of std::pair<const Key, T>?
Thanks and regards

``erase()`` function that takes an iterator but does not return one.

The following program structure can lead to a quadratic computational cost as a function of N.

tls::robin_map<int, int> map;
map.min_load_factor(.1);

// Insert N elements into a map
for (int i = 0; i < N; ++i)
    map[i] = i;

for (int i = 0; i < N; ++i) {
    auto it = map.find(i);
    if (it != map.end()) {
        map.erase(it);
    }
}

The problem is that robin_map::erase returns an iterator. To create a valid return value iterator, the implementation walks to the next bucket if the current one is empty, which can take a long time if the map is nearly empty. It also sets a hint that re-hashing may be in order, but it doesn't re-hash the map itself even if the load factor drops below the threshold.

Here is the code from robin_hash.h

  iterator erase(iterator pos) {
    erase_from_bucket(pos);

    /**
     * Erase bucket used a backward shift after clearing the bucket.
     * Check if there is a new value in the bucket, if not get the next
     * non-empty.
     */
    if (pos.m_bucket->empty()) {
      ++pos;       // <<----------------- THIS IS THE EXPENSIVE STEP
    }

    m_try_shrink_on_next_insert = true;

    return pos;
  }

The example above is of course somewhat contrived, but in real applications, there are often usage patterns involving batch-insertion and batch-erasure of many elements. The batch-erasure can then easily suffer from this O(n^2) cost. A user of nanobind reported an issue in which they were able to trigger this performance issue. I worked around the problem but am not happy with the fix, hence the ticket here.

What I would really like to do is to call erase_from_bucket on the underlying m_ht (tsl::robin_hash) member, but this function is private and I don't see how to get to it. An other option is to call size_t erase(K key) or size_t erase(K key, size_t hash), which don't suffer from the problem because they do not return an iterator. This works but is inefficient when we already have an iterator (for example, because the application using robin_map needed to examine the value before deciding to erase it). The size_t erase(K key, size_t hash) variant then does redundant work by calling find_impl a second time to walk through neighboring buckets.

So this is a long-winded way of asking:

  • Would you consider the addition of an erase(iterator) function that does not return an iterator? I am happy to add the PR for it.
  • Alternatively, would you consider adding a way to access the m_ht implementation detail?

Member function value() for non-const iterator possibly should be const

In most of the standard containers constness of iterator functions determined by - is iterator continues to point to the same element or not. So operator* of non-const iterator is const even if you can change underlying value through it.

TLDR: following should compile imo:

tsl::robin_map<int, int> v = {{0, 0}};
const auto it = v.begin ();
it.value () = 1;

CMake: not exporting if IS_SUBPROJECT breaks use through FetchContent

PR #60 added the follwing lines to CMakeLists.txt :

set(IS_SUBPROJECT TRUE)
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
  set(IS_SUBPROJECT FALSE)
endif()

# Installation (only compatible with CMake version >= 3.3)
if(NOT IS_SUBPROJECT AND ${CMAKE_VERSION} VERSION_GREATER "3.2")

If I include robin-map in my project with FetchContent, like this :

FetchContent_Declare(
  robin-map
  GIT_REPOSITORY https://github.com/tessil/robin-map.git
  GIT_TAG        master
)
FetchContent_MakeAvailable(robin-map)

Then add a public dependency :

target_link_libraries(${PROJECT_NAME}
  INTERFACE
    tsl::robin_map
)

I now get the following message :

CMake Error: install(EXPORT "myLibTargets" ...) includes target "myLib" which requires target "robin_map" that is not in any export set.

Issue #59 mentionned Catch2 as a way to implement the change in #60 but I think the reason it works in Catch2 is because Catch2 is included as part of the private interface of executables (since it is used to produce unit test executables, not libraries)

Anyway, a way to correct #60 would be to let the user decide whether he wants to install or not. In fmt, they use the FMT_INSTALL option.

can't create a map of non-copyable value

In my benchmark I use this data structure as a value:

struct BigData {
	std::shared_ptr<int> a;
	std::map<int, int> b;
	std::vector<double> c;
	std::unique_ptr<int> d;
	std::fstream f;
};

Using tsl::robin_map<int, BigData> gives this compile error:

src/maps/06_tsl/robin_hash.h:157:13: error: use of deleted function ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = int; _T2 = std::basic_fstream<char>]’
             ::new (static_cast<void*>(std::addressof(m_value))) value_type(other.value());
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

hopscotch_map::find() returns an iterator where the value_type is const

Issue:
In difference to std::unordered_map, the following does not compile:

auto m = tsl::hopscotch_map<int, int>{};
m[12] = 12;
m.find(12)->second = 13; // <- second is const, does not compile

Usage:
In difference to "m.at(12) = 13" using std::unordered_map::find(), using find does not perform a out-of-bounds check, thus resulting in slightly better performance if you know the key exists. It seems to me looking at the source that hopscotch does perform an out-of-bounds check using at(), whereas find does not.
If there is another way to access a mutable value while skipping the out-of-bounds check, (I suppose operator[] has some extra logic), I haven't been able to find it.

Compiler: Visual Studio 15.7.3

Update: I intended to post this for the tsl::hop_scotch map rather than robin_map, but as I suppose this issue goes for all the TSL maps, I save you some administration and letting it be here

Thanks for a good set hash map implementations btw.
Best regards/Viktor Sehr

Identity hash function by default on GCC

I ran the following code built with GCC in Linux VM:

#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include <tsl/robin_set.h>

int main() {
    for (uint64_t size = 1 << 20; size <= 1 << 25; size <<= 1) {
        for (int mode = 0; mode < 2; mode++) {
            tsl::robin_set<uint64_t> x;
        
            uint64_t startTime = clock();
            for (uint64_t i = 0; i < size; i++)
                x.insert(i * (mode == 0 ? 0xDEADBEEF : 1 << 20));
            uint64_t endTime = clock();

            double deltaTime = double(endTime - startTime) / CLOCKS_PER_SEC;
            uint64_t sum = 0;
            for (uint64_t val : x)
                sum += val;
        
            printf("N = %llu %c:    time = %0.3lf    chk = %llu\n", size, (mode ? 'B' : 'M'), deltaTime, sum);
        }
    }
    return 0;
}

and I got:

N = 1048576 M:    time = 0.043    chk = 6257894696195457024
N = 1048576 B:    time = 8.280    chk = 576460202547609600
N = 2097152 M:    time = 0.115    chk = 6588752116096958464
N = 2097152 B:    time = 19.110    chk = 2305841909702066176
N = 4194304 M:    time = 0.169    chk = 7916099200727646208
N = 4194304 B:    time = 35.500    chk = 9223369837831520256
N = 8388608 M:    time = 0.354    chk = 13233322349299761152
Killed

So I guess inserting integers divisible by 2^20 takes quadratic time.
Moreover, trying to insert 16M values results in a crash.
Most likely because std::hash<uint64_t>(x) = x on GCC.

Note that I used default settings and got no warnings!
Awful hash function by default is rather critical issue for people who don't know much about hashing (and would probably do worse trying to implement their own hash function or hash table). And given that TSL interface is very STL-like, I think that's the audience it is targeted at.


A proper hash function usually contains three parts:

  1. Combining: getting one integer out of many values/tuples/sequences/etc.
  2. Finalizing: doing some transformation for good statistical properties after step 3.
  3. Reduction: reducing the domain from something like whole range of uint64_t to an index in hash table.

As usual, C++ standard is not precise enough, and STL is not cross-platform.
On MSVC, std::hash performs steps 1 and 2, while std::unordered_set only does step 3.
On GCC, std::hash only performs step 1, while std::unordered_set does steps 2 and 3.
It means that if you use std::hash directly, then you should run your own hash finalizer. TSL hash table only does step 3, but uses std::hash, meaning that the crucial step 2 is missed.

Replace mod_growth_policy with multiplication-based one

Well-known technique to mix bits up and then extract some:

bucket_for_hash(hash) = (hash*CONST) >> SHIFT

CONST should be even odd and preferably prime number, constant through the set/map life. Value of SHIFT determined by the current buckets_count. Extra bonus is that on the hash table growth/reduce table entries arу projected into almost the same place, allowing f.e. incremental, multi-threaded implementation of the resizing operation. Taking into account that shift require 1 cpu cycle, multiplication is 3 cycles, but division is 20+ cycles, MUL-based hashing may be much faster than DIV-based one.

release the latest version to conda-forge?

thank you for making this wonderful library.
I just found that the current release in conda-forge is from the date 06/22/2020. There are a lot of missing features, such as serialization. Would anyone like to help to release the latest version in conda-forge?

Ceral with robin-map

Hi,

I'd like to use tsl::robin_map everywhere instead of the std maps, however I can't figure out how to serialize them with Cereal. I saw your post about sparse_map but don't see the same serialize functions in robin - am I missing something?

Thanks for your help!

1.0.0 fails to build with GCC 12 on Fedora

Kind of a strange error to me but here it is:

[100%] Linking CXX executable tsl_robin_map_tests
/usr/bin/cmake -E cmake_link_script CMakeFiles/tsl_robin_map_tests.dir/link.txt --verbose=1
/usr/bin/g++ -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1  -m64  -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -Wl,-z,relro -Wl,--as-needed  -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1  -Wl,--build-id=sha1 -Wl,-dT,/builddir/build/BUILD/robin-map-1.0.0/.package_note-robin-map-1.0.0-1.fc37.x86_64.ld CMakeFiles/tsl_robin_map_tests.dir/main.cpp.o CMakeFiles/tsl_robin_map_tests.dir/custom_allocator_tests.cpp.o CMakeFiles/tsl_robin_map_tests.dir/policy_tests.cpp.o CMakeFiles/tsl_robin_map_tests.dir/robin_map_tests.cpp.o CMakeFiles/tsl_robin_map_tests.dir/robin_set_tests.cpp.o -o tsl_robin_map_tests  /usr/lib64/libboost_unit_test_framework.so
/usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/12/../../../../lib64/Scrt1.o: in function `_start':
(.text+0x1b): undefined reference to `main'

very slow clustering

I use the follow code to generate test data and insert into robin_map.h, it's quite slow because of clustering.
but this is not happen in my hash_table
https://github.com/ktprime/ktprime/blob/master/hash_table6.hpp

static uint32_t get32rand()
{
return (rand() ^ (rand() << 15u) ^ (rand() << 30u));
}

static uint64_t get64rand()
{
return (((uint64_t)get32rand()) << 32) | get32rand();
}

int setRandTest(uint32_t size, std::vector<uint64_t>& rankdata)
{
rankdata.reserve(size);

uint32_t step = (get32rand() % 64 + 1);
auto s = get64rand();
auto k = s;
for (size_t i = 0; i < size; i ++) {
	k ++;
	if (get32rand() % 1024 == 0)
		k += step;

	rankdata.emplace_back(k);
}

}

tsl::robin_map <uint64_t, int, std::hash<uint64_t>> robin_hashmap;

std::vector<uint64_t> rand_data
setRandTest(1000* 2000, rand_data);
for (auto& v : rand_data)
robin_hashmap[v] = 0;

clear() does not trigger rehash

I re-use an existing robin_set after calling clear(). When I profiled this code, I noticed that a lot of time was spent in the iterator. Consider the following example:

tsl::robin_set<size_t> my_set;
my_set.min_load_factor(0.1f);

for (size_t i = 0; i < 50000000; ++i) {
	my_set.insert(i);
}

std::cout << "Inserted items" << std::endl;

my_set.clear();

for (size_t i = 0; i < 10; ++i) {
	my_set.insert(9999999);
	for (size_t j : my_set) {
		std::cout << "Iterating over " << j << std::endl;
	}
	my_set.clear();
}

I would have expected that the second loop is very fast. However, it needs time proportional to the number of items I have inserted previously. What seems to happen is that only an erase() triggers rehashing on the next insertion, while calling clear() does not trigger any rehashing. Is there any reason for this?

robin_hash crash

Crash Stack

********** Crash dump: **********
Build fingerprint: HUAWEI/PAR-AL00/HWPAR:9/HUAWEIPAR-AL00/9.1.0.353C00:user/release-keys
pid: 28085, tid: 28085, name: main  >>> com.package.name <<<
signal: 11 (SIGSEGV), code: 1 (SEGV_MAPERR) fault addr: 0x0
Stack frame #00    pc 00000000001a22fc    /data/data/com.package.name/files/dynamic/arm64-v8a/libfilament-jni.so [arm64-v8a::14b263bd33fdfae4d889debef057dee0]: Routine tsl::detail_robin_hash::bucket_entry<std::__ndk1::pair<utils::Entity, unsigned int>, false>::dist_from_ideal_bucket() const at /Users/admin/Desktop/sources/filament-src/third_party/robin-map/tnt/../tsl/robin_hash.h:228
Stack frame #01    pc 00000000001a2198    /data/data/com.package.name/files/dynamic/arm64-v8a/libfilament-jni.so [arm64-v8a::14b263bd33fdfae4d889debef057dee0]: Routine tsl::detail_robin_hash::robin_hash<std::__ndk1::pair<utils::Entity, unsigned int>, tsl::robin_map<utils::Entity, unsigned int, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::KeySelect, tsl::robin_map<utils::Entity, unsigned int, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::ValueSelect, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::robin_iterator<true> tsl::detail_robin_hash::robin_hash<std::__ndk1::pair<utils::Entity, unsigned int>, tsl::robin_map<utils::Entity, unsigned int, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::KeySelect, tsl::robin_map<utils::Entity, unsigned int, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::ValueSelect, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::find_impl<utils::Entity>(utils::Entity const&, unsigned long) const at /Users/admin/Desktop/sources/filament-src/third_party/robin-map/tnt/../tsl/robin_hash.h:1027
Stack frame #02    pc 00000000001a2134    /data/data/com.package.name/files/dynamic/arm64-v8a/libfilament-jni.so [arm64-v8a::14b263bd33fdfae4d889debef057dee0]: Routine tsl::detail_robin_hash::robin_hash<std::__ndk1::pair<utils::Entity, unsigned int>, tsl::robin_map<utils::Entity, unsigned int, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::KeySelect, tsl::robin_map<utils::Entity, unsigned int, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::ValueSelect, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::robin_iterator<true> tsl::detail_robin_hash::robin_hash<std::__ndk1::pair<utils::Entity, unsigned int>, tsl::robin_map<utils::Entity, unsigned int, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::KeySelect, tsl::robin_map<utils::Entity, unsigned int, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::ValueSelect, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::find<utils::Entity>(utils::Entity const&) const at /Users/admin/Desktop/sources/filament-src/third_party/robin-map/tnt/../tsl/robin_hash.h:898
Stack frame #03    pc 00000000001a2058    /data/data/com.package.name/files/dynamic/arm64-v8a/libfilament-jni.so [arm64-v8a::14b263bd33fdfae4d889debef057dee0]: Routine tsl::robin_map<utils::Entity, unsigned int, std::__ndk1::hash<utils::Entity>, std::__ndk1::equal_to<utils::Entity>, std::__ndk1::allocator<std::__ndk1::pair<utils::Entity, unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2ul> >::find(utils::Entity const&) const at /Users/admin/Desktop/sources/filament-src/third_party/robin-map/tnt/../tsl/robin_map.h:492
Stack frame #04    pc 00000000001a7438    /data/data/com.package.name/files/dynamic/arm64-v8a/libfilament-jni.so [arm64-v8a::14b263bd33fdfae4d889debef057dee0]: Routine utils::SingleInstanceComponentManager<filament::Box, unsigned char, filament::FRenderableManager::MorphWeights, unsigned char, unsigned short, filament::FRenderableManager::Visibility, utils::Slice<filament::FRenderPrimitive, unsigned int>, filament::FRenderableManager::Bones, utils::Slice<filament::FRenderableManager::MorphTargets, unsigned int> >::getInstance(utils::Entity) const at /Users/admin/Desktop/sources/filament-src/libs/utils/include/utils/SingleInstanceComponentManager.h:93
Stack frame #05    pc 00000000001a6268    /data/data/com.package.name/files/dynamic/arm64-v8a/libfilament-jni.so [arm64-v8a::14b263bd33fdfae4d889debef057dee0]: Routine filament::FRenderableManager::getInstance(utils::Entity) const at /Users/admin/Desktop/sources/filament-src/filament/src/components/RenderableManager.h:90
Stack frame #06    pc 00000000001a621c    /data/data/com.package.name/files/dynamic/arm64-v8a/libfilament-jni.so (_ZNK8filament17RenderableManager11getInstanceEN5utils6EntityE+44) [arm64-v8a::14b263bd33fdfae4d889debef057dee0]: Routine filament::RenderableManager::getInstance(utils::Entity) const at /Users/admin/Desktop/sources/filament-src/filament/src/RenderableManager.cpp:36
Stack frame #07    pc 0000000000190420    /data/data/com.package.name/files/dynamic/arm64-v8a/libfilament-jni.so (Java_com_google_android_filament_RenderableManager_nGetInstance+16) [arm64-v8a::14b263bd33fdfae4d889debef057dee0]: Routine Java_com_google_android_filament_RenderableManager_nGetInstance at /Users/admin/Desktop/sources/filament-src/android/filament-android/src/main/cpp/RenderableManager.cpp:43

image

How can i to avoid this crash?

What is the proper way to use robin_map in lambda function for parallelizing?

Thank you for the great work

As written in the title, I want to use the robin_map instead of std::unordered_map in lambda function.
Jumping into the code right away, the code below works well with std::unordered_map.

struct DataType
{
  bool to_be_del = false;
  DataType(){};
}

unordered_map<KeyType, DataType> candidates;
tbb::parallel_for_each(candidates.begin(), candidates.end(), [&](auto &cand_)
{
  if (some_condition)
  {
    cand_.second.to_be_del = true;
  }
});

But, as robin_map returns const iterator, the below code cannot be compiled.

tsl::robin_map<KeyType, DataType> candidates;
tbb::parallel_for_each(candidates.begin(), candidates.end(), [&](auto &cand_)
{
  if (some_condition)
  {
    cand_.second.to_be_del = true; // modifying const iterator
    cand_.value().to_be_del = true; // errors: cand_ has no member function named 'value()'
  }
});

As can be seen, I want to use tbb::parallel_for or tbb::parallel_for_each for faster computations.
So I would like to ask if there is a proper way to use robin_map and capture the iterator to modify the value within the lambda function!

Thank you in advance.

poor performance of robin_map<int,void *> on insert using gcc

Following on from the recently found issue, I have noted that when using an "int" key on Linux the insert performance is terrible compared to Windows. A quick test shows that robin_map<int,void*> is 2x faster than unordered_map<int,void*> on insert on Windows (Visual Studio 2019) but on Linux GCC (4.9) robin_map is at least 10x slower than unordered_map<int,void *> Same for 'long' as the key.
Is this due to poor performance of the std::hash functions?

aligned_storage is deprecated by C++23

template <typename ValueType, bool StoreHash>
class bucket_entry : public bucket_entry_hash {
using bucket_hash = bucket_entry_hash;

public:
using value_type = ValueType;
using distance_type = std::int16_t;

..................................................................................

private:
using storage = typename std::aligned_storage<sizeof(value_type),
alignof(value_type)>::type;

distance_type m_dist_from_ideal_bucket;
bool m_last_bucket;
storage m_value;
};

visual studio 2022 v17.3.0 preview reported this as error.

Severity Code Description Project File Line Suppression State
Error C4996 'std::aligned_storage<8,4>': warning STL4034: std::aligned_storage and std::aligned_storage_t are deprecated in C++23. Prefer alignas(T) std::byte t_buff[sizeof(T)]. You can define _SILENCE_CXX23_ALIGNED_STORAGE_DEPRECATION_WARNING or _SILENCE_ALL_CXX23_DEPRECATION_WARNINGS to acknowledge that you have received this warning. MDTest D:\STHFT\3rd\include\tsl\robin_hash.h 334

Error with gcc 9.0.1: implicitly-declared...

There's lots of these so I'm only pasting on here

 /builddir/build/BUILD/robin-map-0.6.0/tests/robin_map_tests.cpp:269:53: error: implicitly-declared 'tsl::detail_robin_hash::robin_hash<std::pair<std::__cxx11::basic_string<char>, move_only_test>, tsl::robin_map<std::__cxx11::basic_string<char>, move_only_test>::KeySelect, tsl::robin_map<std::__cxx11::basic_string<char>, move_only_test>::ValueSelect, std::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> >, std::allocator<std::pair<std::__cxx11::basic_string<char>, move_only_test> >, false, tsl::rh::power_of_two_growth_policy<2> >::robin_iterator<false>& tsl::detail_robin_hash::robin_hash<std::pair<std::__cxx11::basic_string<char>, move_only_test>, tsl::robin_map<std::__cxx11::basic_string<char>, move_only_test>::KeySelect, tsl::robin_map<std::__cxx11::basic_string<char>, move_only_test>::ValueSelect, std::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> >, std::allocator<std::pair<std::__cxx11::basic_string<char>, move_only_test> >, false, tsl::rh::power_of_two_growth_policy<2> >::robin_iterator<false>::operator=(const tsl::detail_robin_hash::robin_hash<std::pair<std::__cxx11::basic_string<char>, move_only_test>, tsl::robin_map<std::__cxx11::basic_string<char>, move_only_test>::KeySelect, tsl::robin_map<std::__cxx11::basic_string<char>, move_only_test>::ValueSelect, std::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> >, std::allocator<std::pair<std::__cxx11::basic_string<char>, move_only_test> >, false, tsl::rh::power_of_two_growth_policy<2> >::robin_iterator<false>&)' is deprecated [-Werror=deprecated-copy]

The is on Fedora Rawhide x86_64 with gcc 9.0.1.

it = map.erase(it) potentially passes entries twice

I've stumbled upon a bug in my robin_hood map which also uses backward shift deleting, and wondered how you solve this issue in tsl::robin_map, and it seems you have exactly the same issue. (see martinus/robin-hood-hashing#42)

The problem is that due to backward shift deleting, when iterating a map and erasing elements, it is possible that the same elements will be iterated over twice.

Here is a reproducer, where I insert two elements into the map. When erasing the second element, the first element will wrap around and be iterated over a second time. I currently think it is best to just claim that's the way it is, because I am afraid there is no easy workaround (except not using backward shift deleting)

template <typename T>
struct BadHash {
    size_t operator()(T const& val) const {
        return static_cast<size_t>(val);
    }
};

int main(int, char**) {
    tsl::robin_map<int, int, BadHash<int>> map;
    map.reserve(16); // resizes with mask 31

    map.emplace(31, 1);        // gets into last bucket
    map.emplace(1024 + 31, 2); // would also get into last bucket, but wraps around

    // first entry in the map
    auto it = map.begin();
    std::cout << it->first << "->" << it->second << std::endl;

    // second entry in the map, erase it
    ++it;
    std::cout << it->first << "->" << it->second << std::endl;
    it = map.erase(it);

    // now all two elements are iterated, we should be at the map's end, but due to backward shift
    // deletion we are not
    if (it != map.end()) {
        std::cout << it->first << "->" << it->second << std::endl;
    }
    it = map.erase(it);
    std::cout << "are we now at the end? " << (it == map.end()) << std::endl;
}

I wonder what you think about that issue?

Hash Map Insert Stuck in an infinite loop

Hi, there,

We used the robin-map/v0.6.1, and we observed the robin map stuck in an infinite loop here in insert_value_impl method.

We created tsl::robin_map<uint64_t, DataBlock*>, and we inserted monotonically increasing keys like: 0xc000000000001, 0xc000000000002, etc. We do not have concurrent modifications on the robin map.

We observed this issue both on Intel and ARM machines, but ARM machines showed up more often. However, we do not have a good reproducer, and we only observed this issue in our production fleet.

Could any one help us debug a bit to see if anything pops out? Any suggestions would be appreciated!

Thanks very much!


Here are some debug information we gathered.

The robin map has 8 buckets, but the number of elements are only 5 (although all 8 keys look valid to us). However, we observed a really large m_load_threshold = 18446744069414584320, and all 8 buckets show with m_dist_from_ideal_bucket = 32767.

We suspected somehow m_load_threshold overflowed, causing the robin map would not expand the size, and hence there are no empty buckets in the map anymore.

(gdb) p *this 
$35 = {<std::hash<unsigned long>> = {<std::__hash_base<unsigned long, unsigned long>> = {<No data fields>}, <No data fields>}, <std::equal_to<unsigned long>> = {<std::binary_function<unsigned long, unsigned long, bool>> = {<No data fields>}, <No data fields>}, <tsl::rh::power_of_two_growth_policy<2>> = {m_mask = 7}, 
  static STORE_HASH = false, static USE_STORED_HASH_ON_LOOKUP = false, static DEFAULT_INIT_BUCKETS_SIZE = 0, static DEFAULT_MAX_LOAD_FACTOR = 0.5, 
  static MINIMUM_MAX_LOAD_FACTOR = 0.200000003, static MAXIMUM_MAX_LOAD_FACTOR = 0.949999988, static DEFAULT_MIN_LOAD_FACTOR = 0, static MINIMUM_MIN_LOAD_FACTOR = 0, 
  static MAXIMUM_MIN_LOAD_FACTOR = 0.150000006, static REHASH_ON_HIGH_NB_PROBES__NPROBES = 128, static REHASH_ON_HIGH_NB_PROBES__MIN_LOAD_FACTOR = 0.150000006, 
  m_buckets_data = {<std::_Vector_base<tsl::detail_robin_hash::bucket_entry<std::pair<unsigned long, MapGraph::Blocks::DataBlock*>, false>, std::allocator<tsl::detail_robin_hash::bucket_entry<std::pair<unsigned long, MapGraph::Blocks::DataBlock*>, false> > >> = {
      _M_impl = {<std::allocator<tsl::detail_robin_hash::bucket_entry<std::pair<unsigned long, MapGraph::Blocks::DataBlock*>, false> >> = {<__gnu_cxx::new_allocator<tsl::detail_robin_hash::bucket_entry<std::pair<unsigned long, MapGraph::Blocks::DataBlock*>, false> >> = {<No data fields>}, <No data fields>}, _M_start = 0x40006630f040, 
        _M_finish = 0x40006630f100, _M_end_of_storage = 0x40006630f100}}, <No data fields>}, m_buckets = 0x40006630f040, m_bucket_count = 8, m_nb_elements = 5, 
  m_load_threshold = 18446744069414584320, m_max_load_factor = 0.5, m_grow_on_next_insert = true, m_min_load_factor = 0, m_try_skrink_on_next_insert = false}

(gdb) p m_buckets[0]
$36 = {<tsl::detail_robin_hash::bucket_entry_hash<false>> = {<No data fields>}, static EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1, m_dist_from_ideal_bucket = 32767, 
  m_last_bucket = false, m_value = {__data = "\t\000\000\000\000\000\f\000\000\000\000\000\000\000\000", __align = {<No data fields>}}}
(gdb) p m_buckets[1]
$37 = {<tsl::detail_robin_hash::bucket_entry_hash<false>> = {<No data fields>}, static EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1, m_dist_from_ideal_bucket = 32767, 
  m_last_bucket = false, m_value = {__data = "\002\000\000\000\000\000\f\000\340R\304#\v@\000", __align = {<No data fields>}}}
(gdb) p m_buckets[2]
$38 = {<tsl::detail_robin_hash::bucket_entry_hash<false>> = {<No data fields>}, static EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1, m_dist_from_ideal_bucket = 32767, 
  m_last_bucket = false, m_value = {__data = "\003\000\000\000\000\000\f\000\320P\304#\v@\000", __align = {<No data fields>}}}
(gdb) p m_buckets[3]
$39 = {<tsl::detail_robin_hash::bucket_entry_hash<false>> = {<No data fields>}, static EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1, m_dist_from_ideal_bucket = 32767, 
  m_last_bucket = false, m_value = {__data = "\004\000\000\000\000\000\f\000А?\034\v@\000", __align = {<No data fields>}}}
(gdb) p m_buckets[4]
$40 = {<tsl::detail_robin_hash::bucket_entry_hash<false>> = {<No data fields>}, static EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1, m_dist_from_ideal_bucket = 32767, 
  m_last_bucket = false, m_value = {__data = "\005\000\000\000\000\000\f\000\000\221?\034\v@\000", __align = {<No data fields>}}}
(gdb) p m_buckets[5]
$41 = {<tsl::detail_robin_hash::bucket_entry_hash<false>> = {<No data fields>}, static EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1, m_dist_from_ideal_bucket = 32767, 
  m_last_bucket = false, m_value = {__data = "\006\000\000\000\000\000\f\000\300$Fe\000@\000", __align = {<No data fields>}}}
(gdb) p m_buckets[6]
$42 = {<tsl::detail_robin_hash::bucket_entry_hash<false>> = {<No data fields>}, static EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1, m_dist_from_ideal_bucket = 32767, 
  m_last_bucket = false, m_value = {__data = "\a\000\000\000\000\000\f\000\240&Fe\000@\000", __align = {<No data fields>}}}
(gdb) p m_buckets[7]
$43 = {<tsl::detail_robin_hash::bucket_entry_hash<false>> = {<No data fields>}, static EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1, m_dist_from_ideal_bucket = 32767, 
  m_last_bucket = true, m_value = {__data = "\b\000\000\000\000\000\f\000\340\033Re\000@\000", __align = {<No data fields>}}}

Here are the list of keys in the map:

(gdb) p/x *reinterpret_cast<uint64_t*>(m_buckets[0].m_value.__data)
$14 = 0xc000000000009
(gdb) p/x *reinterpret_cast<uint64_t*>(m_buckets[1].m_value.__data)
$15 = 0xc000000000002
(gdb) p/x *reinterpret_cast<uint64_t*>(m_buckets[2].m_value.__data)
$16 = 0xc000000000003
(gdb) p/x *reinterpret_cast<uint64_t*>(m_buckets[3].m_value.__data)
$17 = 0xc000000000004
(gdb) p/x *reinterpret_cast<uint64_t*>(m_buckets[4].m_value.__data)
$18 = 0xc000000000005
(gdb) p/x *reinterpret_cast<uint64_t*>(m_buckets[5].m_value.__data)
$19 = 0xc000000000006
(gdb) p/x *reinterpret_cast<uint64_t*>(m_buckets[6].m_value.__data)
$20 = 0xc000000000007
(gdb) p/x *reinterpret_cast<uint64_t*>(m_buckets[7].m_value.__data)
$21 = 0xc000000000008

Excessive use of memory in some odd cases

I was trying to use heaptrack (a memory profiler) and it ended up using excessive amounts of memory. After investigation, this turned out to be from excessive use of memory by robin_set.
The original bug: https://bugs.kde.org/show_bug.cgi?id=477018
A reduced reproducer, with code extracted from heaptrack:

#include <iostream>
#include <cinttypes>
#include "robin_set.h"

struct IndexedAllocationInfo {
	uint64_t size;
	uint32_t index;

	bool operator==(const IndexedAllocationInfo& rhs) const {
            return rhs.index == index && rhs.size == size;
	}
};

// see: https://stackoverflow.com/a/2595226/35250
template <class T>
inline void hashCombine(std::size_t& seed, const T& v)
{
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

namespace std {
template <>
struct hash<IndexedAllocationInfo>
{
    size_t operator()(const IndexedAllocationInfo& info) const
    {
        size_t seed = 0;
        hashCombine(seed, info.size);
        hashCombine(seed, info.index);
        return seed;
    }
};
}

int main() {
	tsl::robin_set<IndexedAllocationInfo> set;
	uint64_t size;
	uint32_t index;
	while (std::cin >> size >> index) {
		set.insert({size, index});
	}
	return 0;
}

The program reads a log of the data that was fed into the robin-set that used too much memory and replays it.
The log is heaptrack.log.gz
Save the source above as test.cc and compile with g++ -o test test.cc -I/path/to/include/tsl.
Replay the log with zcat heaptrack.log.gz | ./test.
There are "only" slightly over 2M entries, and after about 470k, memory usage jumps above 40GB.
std::unordered_set, on the other hand, doesn't bat an eye.

It certainly doesn't help that 159,878 of the entries have /some/ collision (without even doing a modulo).

CMake exports using incorrect name

Trying to add robin-map into a cmake script of another package using find_package, with the outputs generated by the cmake script of this project, throws this error:

...
find_package(tsl-robin-map PATHS "<my path>")
if (tsl-robin-map_FOUND)
    target_link_libraries(my_project PRIVATE tsl::robin-map)
endif()
CMake Error at <my path>/robinmap/build/tsl-robin-mapConfig.cmake:31 (include):
  include could not find load file:

    <my path>/robinmap/build/tsl-robin-mapTargets.cmake
Call Stack (most recent call first):
  CMakeLists.txt:71 (find_package)


CMake Error at <my path>/robinmap/build/tsl-robin-mapConfig.cmake:32 (get_target_property):
  get_target_property() called with non-existent target "tsl::robin_map".

Infinite recursive loop in robin_set::insert

When compiling with Visual C++ 2017 15.7.3 I get this warning. Note that the "C++ Language Setting" of "Conformance Mode" must be "No". If Conformance mode is /permissive- then the code compiles and runs without error. Clearly this is an issue with standards conformance and the Visual C++ compiler switches. But is an easy trap to fall into unless you are using the latest Visual C++ compilers with correct settings.

Warning C4717 'tsl::detail_robin_hash::robin_hash<CBroadcaster *,tsl::robin_set<CBroadcaster *,std::hash<CBroadcaster *>,std::equal_to<CBroadcaster *>,std::allocator<CBroadcaster *>,0,tsl::rh::power_of_two_growth_policy<2> >::KeySelect,void,std::hash<CBroadcaster *>,std::equal_to<CBroadcaster *>,std::allocator<CBroadcaster *>,0,tsl::rh::power_of_two_growth_policy<2> >::insert_value<CBroadcaster * &>': recursive on all control paths, function will cause runtime stack overflow foundation tsl\robin_hash.h 1152

And so it happens. I get a stack overflow run-time error due to a recursive loop when inserting values.

struct CBroadcaster
{
int dummy;
};

int main()
{
tsl::robin_set<CBroadcaster *> aSet;
for (int i = 0; i < 100; i++) {
CBroadcaster *tmp = new CBroadcaster();
aSet.insert(tmp);
}
}

find/insert return iterators with read-only second element

Dear Author(s), I am getting the following compilation error when trying to modify map-value when using iterators returned by find/insert. Is this expected behavior? Thanks,

Error: error: assignment of member 'std::pair<int, int>::second' in read-only object
Minimal code to reproduce:

tsl::robin_map<int, int> mp;
mp[0] = 0;
mp.find(0)->second = 1;
mp.insert({0, 1}).first->second = 2;

Using omp with robin-map

I'm trying to do some operation on every key-value pair via omp

#pragma omp parallel for
for (const auto& itr : robin_map<key, hash>) {
    //some operation
}

and I'm getting this error

error: no match for ‘operator-’ (operand types are ‘tsl::robin_map<long unsigned int, short unsigned int, fastHash>::iterator’ {aka ‘tsl::detail_robin_hash::robin_hash<std::pair<long unsigned int, short unsigned int>, tsl::robin_map<long unsigned int, short unsigned int, fastHash>::KeySelect, tsl::robin_map<long unsigned int, short unsigned int, fastHash>::ValueSelect, fastHash, std::equal_to<long unsigned int>, std::allocator<std::pair<long unsigned int, short unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2> >::robin_iterator<false>’} and ‘tsl::robin_map<long unsigned int, short unsigned int, fastHash>::iterator’ {aka ‘tsl::detail_robin_hash::robin_hash<std::pair<long unsigned int, short unsigned int>, tsl::robin_map<long unsigned int, short unsigned int, fastHash>::KeySelect, tsl::robin_map<long unsigned int, short unsigned int, fastHash>::ValueSelect, fastHash, std::equal_to<long unsigned int>, std::allocator<std::pair<long unsigned int, short unsigned int> >, false, tsl::rh::power_of_two_growth_policy<2> >::robin_iterator<false>’})

omp uses operator- to find the number of jobs it has to do. Can you implement the operator overload?

Why did the download of release 0.6.3 change?

Conan is a C/C++ package manager

In the Conan Center Index we have a recipe for robin-map: https://conan.io/center/tsl-robin-map/0.6.3/

The downloads of the source code are secured by sha256 checksums and are verified after every download

On June 20 version 0.6.3 was added with the sha256sum 7db3530dd6482c4a0d8f8bb52601dbdfdf3da10ac7ea9e100d4b8041a02f034e

conan-io/conan-center-index@ea4942a
conan-io/conan-center-index#1950 (comment)

Somewhen after June 20, the checksum changed to e6654c8c2598f63eb0b1d52ff8bdf39cfcc91d81dd5d05274a6dca91241cd72f

conan-io/conan-center-index#2568

Why is this?

Question: fixed memory?

I need a hash set in a constrained system where I cannot allocate memory; I have a fixed block available for the hash set, and that's it.

I think this is supported by robin-map, but I just wanted to confirm, and see if I understand the proper way.

The following is what I have in mind:

  • Provide a custom allocator which just returns the singular block of memory I have available.
  • Provide a custom growth policy, which just sets min_bucket_count_in_out to the max value I can support (though apparently I need to leave it 0 if it comes in as 0; not sure why)
    • In this policy, next_bucket_count() == max_bucket_count() == the same value I set min_bucket_count_in_out to in the constructor.

Does that seem right?

Broken on Conan

When installing OpenImageIO I get ERROR: Unable to find 'tsl-robin-map/0.6.1@tessil/stable' in remotes

The link in readme.md seems to be down as well.

serialization using boost

currently, it requires that the serializer have an operator (), but boost serialization does not have that function.

error: type 'boost::archive::text_oarchive' does not provide a call operator.

my code is something like this:

template<class Archive>
void save(Archive& ar, const unsigned int version) const {
    mymap.serialize(ar);
}

Am I not using it correctly?
if not, would you like to consider supporting this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.