|
|
Random number 362043 Test values are 981603 21447 81390 7212 901014 925691 975057 599908 691098 915834 std::vector CPU time used: 0.000000 ms CPU time used: 0.000000 ms CPU time used: 0.001000 ms CPU time used: 0.001000 ms CPU time used: 0.000000 ms CPU time used: 0.000000 ms CPU time used: 0.001000 ms CPU time used: 0.000000 ms CPU time used: 0.001000 ms CPU time used: 0.000000 ms std::unordered_set max size 1152921504606846975 size 1000000 bucket_count 1056323 Load factor 0.946680 Max Load factor 1.000000 CPU time used: 3.275000 ms CPU time used: 0.720000 ms CPU time used: 2.561000 ms CPU time used: 0.065000 ms CPU time used: 2.459000 ms CPU time used: 2.323000 ms CPU time used: 0.711000 ms CPU time used: 2.831000 ms CPU time used: 1.811000 ms CPU time used: 0.983000 ms Test timing std::set CPU time used: 26.807000 ms CPU time used: 0.539000 ms CPU time used: 2.583000 ms CPU time used: 0.183000 ms CPU time used: 26.665000 ms CPU time used: 24.537000 ms CPU time used: 28.095000 ms CPU time used: 16.055000 ms CPU time used: 19.860000 ms CPU time used: 23.903000 ms |
cppreference wrote: |
---|
Unordered set is an associative container that contains a set of unique objects of type Key. Search, insertion, and removal have average constant-time complexity. Internally, the elements are not sorted in any particular order, but organized into buckets. Which bucket an element is placed into depends entirely on the hash of its value. This allows fast access to individual elements, since once a hash is computed, it refers to the exact bucket the element is placed into. std::unordered_set meets the requirements of Container, AllocatorAwareContainer, UnorderedAssociativeContainer. |
TheIdeasMan wrote: |
---|
Also wondering why the unordered_set is about 2'500 times slower for a find than a vector? I mean the worst case scenario for vector is supposed to be O(n), isn't unordered_set supposed to be better than that? I know that trying to predict what is the average complexity is usually quite difficult. |
Test values are 2378152212 2567041987 3441403981 2569341569 170878496 1954053340 1348074434 931938584 1617858511 1988811527 std::vector CPU time used: 0.000000 ms CPU time used: 0.001000 ms CPU time used: 0.000000 ms CPU time used: 0.000000 ms CPU time used: 0.000000 ms CPU time used: 0.001000 ms CPU time used: 0.001000 ms CPU time used: 0.000000 ms CPU time used: 0.001000 ms CPU time used: 0.000000 ms std::unordered_set max size 1152921504606846975 size 999884 bucket_count 1056323 Load factor 0.946570 Max Load factor 1.000000 CPU time used: 3.638000 ms CPU time used: 4.009000 ms CPU time used: 3.529000 ms CPU time used: 3.467000 ms CPU time used: 3.491000 ms CPU time used: 3.416000 ms CPU time used: 3.263000 ms CPU time used: 3.357000 ms CPU time used: 3.371000 ms CPU time used: 3.451000 ms Test timing std::set CPU time used: 28.653000 ms CPU time used: 27.280000 ms CPU time used: 31.278000 ms CPU time used: 27.163000 ms CPU time used: 29.629000 ms CPU time used: 26.345000 ms CPU time used: 28.941000 ms CPU time used: 26.109000 ms CPU time used: 28.429000 ms CPU time used: 25.903000 ms |
|
|
|
|
unordered_set was 3 times faster than vector with n=100 unordered_set was 21.6 times faster than vector with n=1000 unordered_set was 224 times faster than vector with n=10000 unordered_set was 1086.73 times faster than vector with n=100000 unordered_set was 6518.18 times faster than vector with n=1000000 unordered_set was 86467.7 times faster than vector with n=10000000 unordered_set was 655039 times faster than vector with n=100000000 |
|
|
------- clang++/libc++ ---------- 10000000 values, 1000000 searches vector: insert & sort - 1510 msecs. vector: find - 720 msecs. set: insert - 5930 msecs. set: find - 310 msecs. ------- g++/libstdc++ ----------- 10000000 values, 1000000 searches vector: insert & sort - 1200 msecs. vector: find - 770 msecs. set: insert - 3510 msecs. set: find - 210 msecs. |
10000000 values, 1000000 searches vector: insert & sort - 1574 msecs. vector: find - 851 msecs. set: insert - 13011 msecs. set: find - 244 msecs. |
|
|
|
|
|
|
|
|
value
) has to evaluate to 0 or 1 true or false, and this happens with line 14, specifically this part : sizeof( test( std::declval<T>() ) ) == 1
.char
?U&&
is a rvlaue reference, that's pretty obvious. std::declval<U>()
converts the type U into a rvalue. And this is then used as an argument to U's begin iterator. Not sure what the effect of this rvalue is on the call to the begin iterator? Or why one might need an rvalue there?decltype
call, it's argument is perhaps the begin iterator, or maybe T
? Not sure what is being returned, cppreference has this:cppreference wrote: |
---|
Explanation 1) If the argument is an unparenthesized id-expression or an unparenthesized class member access expression, then decltype yields the type of the entity named by this expression. If there is no such entity, or if the argument names a set of overloaded functions, the program is ill-formed. 2) If the argument is any other expression of type T, and a) if the value category of expression is xvalue, then decltype yields T&&; b) if the value category of expression is lvalue, then decltype yields T&; c) if the value category of expression is prvalue, then decltype yields T. |
T
I am guessing the declvalue part must be a prvalue. But then there is * at the end of decltype, so the expression becomes a pointer? Then it has a default value of 0, making the argument optional? key_type
is a member in common with associative containers, so you have taken advantage of that. But the same could not be said for the sequence containers, so there is some gymnastics required on line 10?std::is_same
(If I may): Is there a reason why it didn't work in conjunction with the template parameter? Or was I missing something ?
|
The operands of the four operators typeid, sizeof, noexcept, and decltype (since C++11) are expressions that are not evaluated (unless they are polymorphic glvalues and are the operands of typeid), since these operators only query the compile-time properties of their operands. Thus, std::size_t n = sizeof(std::cout << 42); does not perform console output.http://en.cppreference.com/w/cpp/language/expressions#Unevaluated_expressions |
There is a surprising amount of power in sizeof : You can apply sizeof to any expression, no matter how complex, and sizeof returns its size without actually evaluating that expression at runtime. This means that sizeof is aware of overloading, template instantiation, conversion rules—everything that can take part in a C++ expression. In fact, sizeof conceals a complete facility for deducing the type of an expression; eventually, sizeof throws away the expression and returns only the size of its result. - Alexandrescu in 'Modern C++ Design' |
using two_chars = char[2] ; static two_chars& test(...) ;
is not a template; it is a c-style variadic function. In the C programming language, at least one named parameter must appear before the ellipsis parameter, so printz(...); is not valid. In C++, this form is allowed even though the arguments passed to such function are not accessible, and is commonly used as the fallback overload in SFINAE, exploiting the lowest priority of the ellipsis conversion in overload resolution. (emphasis added) http://en.cppreference.com/w/cpp/language/variadic_arguments |
|
|
using two_chars = char[2] ; static two_chars& test(...) ; // overload 2
If std::begin(x), where the type of the expression x is T, is a valid construct, the overload resolves to overload 1. |
> going back to std::is_same ... Is there a reason why it didn't work in conjunction with the template parameter? The template arguments other than that for the type parameter ContainerData can't be deduced. |
It is not computing the hash that takes a lot of time. Things like dynamic memory allocation, rehashing when load factor becomes more than the maximum load factor, a data representation that is less compact than a vector and the general poorer memory locality have an adverse impact on time. |
|
|