tessil / robin-map Goto Github PK
View Code? Open in Web Editor NEWC++ implementation of a fast hash map and hash set using robin hood hashing
License: MIT License
C++ implementation of a fast hash map and hash set using robin hood hashing
License: MIT License
Awesome library!
Has anybody tried to write a C wrapper?
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!
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?
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?
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;
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?
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?
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
I really like this lib because of its performance. I want to use it to communicate between processes.
How to do?
Thank you!
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:
It works fine with node-based (std::
) maps.
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
)
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.
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.
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:
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)
next_bucket_count()
== max_bucket_count()
== the same value I set min_bucket_count_in_out
to in the constructor.Does that seem right?
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.
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
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.
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!
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);
}
}
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?
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;
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.
I want to use it to communicate between different processes.
Do you have any idea?
Thank you!
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
Hi,
Is there a way to take advantage of the pre-calculated hash when inserting a new entry into robin_map?
Thanks!
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
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".
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).
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.
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 😄
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?
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.
I am using GCC 6.3.1 on Centos 7 and compiling with
c++ test.cpp -o test
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?
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.
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
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.
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.
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?
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?
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'
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! :)
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!
Hello,
Recently a user reported that robin-map is installed when make install
is used with our project.
A check to disable install whe robin-map is a subproject, would be great.
Examples of how a couple of other projects do it -
SDL: https://github.com/libsdl-org/SDL/blob/main/CMakeLists.txt#L3349
Catch2: https://github.com/catchorg/Catch2/blob/devel/CMakeLists.txt#L111
Thanks!
Hello.
Are there any plans to support hashtable "shrink" on erases and reaching sufficiently low load_factor (like maybe 0.125) ?
Does robin_map is thread safe?
Thanks
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
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();
}
}
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
How can i to avoid this crash?
robin_hash::dist_from_ideal_bucket()
is causing segmentation faults when accessed via robin_map::find()
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:
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.
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;
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:
erase(iterator)
function that does not return an iterator? I am happy to add the PR for it.m_ht
implementation detail?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());
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.