File: emplace.cc

package info (click to toggle)
parallel-hashmap 1.4.1%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,872 kB
  • sloc: cpp: 20,492; ansic: 1,114; python: 492; makefile: 85; haskell: 56; perl: 43; sh: 23
file content (166 lines) | stat: -rw-r--r-- 5,548 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include <map>
#include <unordered_map>
#include <string>
#include <iostream>
#include <chrono>
#include <vector>
#include <parallel_hashmap/phmap.h>

#include <sstream>

template <typename T>
using milliseconds = std::chrono::duration<T, std::milli>;

// type containing std::string. Seems to take a long time to construct (and maybe move)
// ------------------------------------------------------------------------------------
class custom_type
{
    std::string one = "one";
    std::string two = "two";
    std::uint32_t three = 3;
    std::uint64_t four = 4;
    std::uint64_t five = 5;
public:
    custom_type() = default;

    // Make object movable and non-copyable
    custom_type(custom_type &&) = default;
    custom_type& operator=(custom_type &&) = default;

    // should be automatically deleted per http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014
    //custom_type(custom_type const&) = delete;
    //custom_type& operator=(custom_type const&) = delete;
};

// type containing only integrals. should be faster to create.
// -----------------------------------------------------------
class custom_type_2
{
    std::uint32_t three = 3;
    std::uint64_t four  = 4;
    std::uint64_t five  = 5;
    std::uint64_t six   = 6;
public:
    custom_type_2() = default;

    // Make object movable and non-copyable
    custom_type_2(custom_type_2 &&) = default;
    custom_type_2& operator=(custom_type_2 &&) = default;

    // should be automatically deleted per http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014
    //custom_type_2(custom_type_2 const&) = delete;
    //custom_type_2& operator=(custom_type_2 const&) = delete;
};

// convert std::size_t to appropriate key
// --------------------------------------
template <class K>
struct GenKey
{
    K operator()(std::size_t j);
};

template <>
struct GenKey<std::string>
{
    std::string operator()(std::size_t j) {
        std::ostringstream stm;
        stm << j;
        return stm.str();
    }
};

template <>
struct GenKey<int>
{
    int operator()(std::size_t j) {
        return (int)j;
    }
};

// emplace key + large struct
// --------------------------
template <class Map, class K, class V, class T> struct _emplace
{
    void operator()(Map &m, std::size_t j);
};

// "void" template parameter -> use emplace
template <class Map, class K, class V> struct _emplace<Map, K, V, void>
{
    void operator()(Map &m, std::size_t j)
    {
        m.emplace(GenKey<K>()(j), V());
    }
};

// "int" template parameter -> use emplace_back for std::vector
template <class Map, class K, class V> struct _emplace<Map, K, V, int>
{
    void operator()(Map &m, std::size_t j)
    {
        m.emplace_back(GenKey<K>()(j), V());
    }
};

// The test itself
// ---------------
template <class Map, class K, class V, class T, template <class, class, class, class> class INSERT>
void _test(std::size_t iterations, std::size_t container_size, const char *map_name)
{
    std::size_t count = 0;
    auto t1 = std::chrono::high_resolution_clock::now();
    INSERT<Map, K, V, T> insert;
    for (std::size_t i=0; i<iterations; ++i)
    {
        Map m;
        for (std::size_t j=0; j<container_size; ++j)
            insert(m, j);
        count += m.size();
    }
    auto t2 = std::chrono::high_resolution_clock::now();
    auto elapsed = milliseconds<double>(t2 - t1).count();
    if (count != iterations*container_size)
        std::clog << "  invalid count: " << count << "\n";
    std::clog << map_name << std::fixed << int(elapsed) << " ms\n";
}


template <class K, class V, template <class, class, class, class> class INSERT>
void test(std::size_t iterations, std::size_t container_size)
{
    std::clog << "bench: iterations: " << iterations <<  " / container_size: "  << container_size << "\n";

    _test<std::map<K, V>,               K, V, void, INSERT>(iterations, container_size, "  std::map:               ");
    _test<std::unordered_map<K, V>,     K, V, void, INSERT>(iterations, container_size, "  std::unordered_map:     ");
    _test<phmap::flat_hash_map<K, V>,   K, V, void, INSERT>(iterations, container_size, "  phmap::flat_hash_map:   ");
    _test<std::vector<std::pair<K, V>>, K, V, int, INSERT> (iterations, container_size, "  std::vector<std::pair>: ");
    std::clog << "\n";
    
}

int main()
{
    std::size_t iterations = 100000;

    // test with custom_type_2 (int key + 32 byte value). This is representative 
    // of the hash table insertion speed.
    // -------------------------------------------------------------------------
    std::clog << "\n\n" << "testing with <int, custom_type_2>"   "\n";
    std::clog << "---------------------------------"   "\n";
    test<int, custom_type_2, _emplace>(iterations,10);
    test<int, custom_type_2, _emplace>(iterations,100);
    test<int, custom_type_2, _emplace>(iterations,500);

    // test with custom_type, which contains two std::string values, and use 
    // a generated string key. This is not very indicative of the speed of the 
    // hash itself, as a good chunk of the time is spent creating the keys and 
    // values (as shown by the long times even for std::vector).
    // -----------------------------------------------------------------------
    std::clog << "\n" << "testing with <string, custom_type>"   "\n";
    std::clog << "---------------------------------"   "\n";
    test<std::string, custom_type, _emplace>(iterations,1);
    test<std::string, custom_type, _emplace>(iterations,10);
    test<std::string, custom_type, _emplace>(iterations,50);

}