@@ -42,15 +42,14 @@ using AllContainerTypes = std::tuple<VectorContainer, SetContainer, ForwardListC
4242
4343// set_intersection performance may depend on where matching values lie
4444enum class OverlapPosition {
45- Nowhere ,
46- Front,
47- Back,
48- Interlaced,
45+ None ,
46+ Front,
47+ Back,
48+ Interlaced,
4949};
5050
5151struct AllOverlapPositions : EnumValuesAsTuple<AllOverlapPositions, OverlapPosition, 4 > {
52- static constexpr const char * Names[] = {
53- " Nowhere" , " Front" , " Back" , " Interlaced" };
52+ static constexpr const char * Names[] = {" None" , " Front" , " Back" , " Interlaced" };
5453};
5554
5655// functor that moves elements from an iterator range into a new Container instance
@@ -59,166 +58,168 @@ struct MoveInto {};
5958
6059template <typename T>
6160struct MoveInto <std::vector<T>> {
62- template <class It >
63- [[nodiscard]] static std::vector<T> operator ()(It first, It last) {
64- std::vector<T> out;
65- std::move (first, last, std::back_inserter (out));
66- return out;
67- }
61+ template <class It >
62+ [[nodiscard]] static std::vector<T> operator ()(It first, It last) {
63+ std::vector<T> out;
64+ std::move (first, last, std::back_inserter (out));
65+ return out;
66+ }
6867};
6968
7069template <typename T>
7170struct MoveInto <std::forward_list<T>> {
72- template <class It >
73- [[nodiscard]] static std::forward_list<T> operator ()(It first, It last) {
74- std::forward_list<T> out;
75- std::move (first, last, std::front_inserter (out));
76- out.reverse ();
77- return out;
78- }
71+ template <class It >
72+ [[nodiscard]] static std::forward_list<T> operator ()(It first, It last) {
73+ std::forward_list<T> out;
74+ std::move (first, last, std::front_inserter (out));
75+ out.reverse ();
76+ return out;
77+ }
7978};
8079
8180template <typename T>
8281struct MoveInto <std::set<T>> {
83- template <class It >
84- [[nodiscard]] static std::set<T> operator ()(It first, It last) {
85- std::set<T> out;
86- std::move (first, last, std::inserter (out, out.begin ()));
87- return out;
88- }
82+ template <class It >
83+ [[nodiscard]] static std::set<T> operator ()(It first, It last) {
84+ std::set<T> out;
85+ std::move (first, last, std::inserter (out, out.begin ()));
86+ return out;
87+ }
8988};
9089
9190// lightweight wrapping around fillValues() which puts a little effort into
9291// making that would be contiguous when sorted non-contiguous in memory
9392template <typename T>
9493std::vector<T> getVectorOfRandom (size_t N) {
95- std::vector<T> V ;
96- fillValues (V , N, Order::Random);
97- sortValues (V , Order::Random);
98- return std::vector<T>(V );
94+ std::vector<T> v ;
95+ fillValues (v , N, Order::Random);
96+ sortValues (v , Order::Random);
97+ return std::vector<T>(v );
9998}
10099
101100// forward_iterator wrapping which, for each increment, moves the underlying iterator forward Stride elements
102101template <typename Wrapped>
103102struct StridedFwdIt {
104- Wrapped Base ;
105- unsigned Stride ;
103+ Wrapped base_ ;
104+ unsigned stride_ ;
106105
107106 using iterator_category = std::forward_iterator_tag;
108- using difference_type = typename Wrapped::difference_type;
109- using value_type = typename Wrapped::value_type;
110- using pointer = typename Wrapped::pointer;
111- using reference = typename Wrapped::reference;
112-
113- StridedFwdIt (Wrapped B, unsigned Stride_) : Base(B), Stride(Stride_) { assert (Stride != 0 ); }
114-
115- StridedFwdIt operator ++() { for (unsigned I=0 ; I<Stride; ++I) ++Base; return *this ; }
116- StridedFwdIt operator ++(int ) { auto Tmp = *this ; ++*this ; return Tmp; }
117- value_type& operator *() { return *Base; }
118- const value_type& operator *() const { return *Base; }
119- value_type& operator ->() { return *Base; }
120- const value_type& operator ->() const { return *Base; }
121- bool operator ==(const StridedFwdIt& o) const { return Base==o.Base ; }
107+ using difference_type = typename Wrapped::difference_type;
108+ using value_type = typename Wrapped::value_type;
109+ using pointer = typename Wrapped::pointer;
110+ using reference = typename Wrapped::reference;
111+
112+ StridedFwdIt (Wrapped base, unsigned stride) : base_(base), stride_(stride) { assert (stride_ != 0 ); }
113+
114+ StridedFwdIt operator ++() {
115+ for (unsigned i = 0 ; i < stride_; ++i)
116+ ++base_;
117+ return *this ;
118+ }
119+ StridedFwdIt operator ++(int ) {
120+ auto tmp = *this ;
121+ ++*this ;
122+ return tmp;
123+ }
124+ value_type& operator *() { return *base_; }
125+ const value_type& operator *() const { return *base_; }
126+ value_type& operator ->() { return *base_; }
127+ const value_type& operator ->() const { return *base_; }
128+ bool operator ==(const StridedFwdIt& o) const { return base_ == o.base_ ; }
122129 bool operator !=(const StridedFwdIt& o) const { return !operator ==(o); }
123130};
124- template <typename Wrapped> StridedFwdIt (Wrapped, unsigned ) -> StridedFwdIt<Wrapped>;
125-
131+ template <typename Wrapped>
132+ StridedFwdIt (Wrapped, unsigned ) -> StridedFwdIt<Wrapped>;
126133
127134// realistically, data won't all be nicely contiguous in a container
128135// we'll go through some effort to ensure that it's shuffled through memory
129136template <class Container >
130- std::pair<Container, Container> genCacheUnfriendlyData (size_t Size1 , size_t Size2 , OverlapPosition Pos ) {
137+ std::pair<Container, Container> genCacheUnfriendlyData (size_t size1 , size_t size2 , OverlapPosition pos ) {
131138 using ValueType = typename Container::value_type;
132- const MoveInto<Container> moveInto;
133- const auto SrcSize = Pos == OverlapPosition::Nowhere ? Size1 + Size2 : std::max (Size1, Size2);
134- std::vector<ValueType> Src = getVectorOfRandom<ValueType>(SrcSize);
135-
136- if (Pos == OverlapPosition::Nowhere) {
137- std::sort (Src.begin (), Src.end ());
138- return std::make_pair (
139- moveInto (Src.begin (), Src.begin () + Size1),
140- moveInto (Src.begin () + Size1, Src.end ()));
139+ const MoveInto<Container> move_into;
140+ const auto src_size = pos == OverlapPosition::None ? size1 + size2 : std::max (size1, size2);
141+ std::vector<ValueType> src = getVectorOfRandom<ValueType>(src_size);
142+
143+ if (pos == OverlapPosition::None) {
144+ std::sort (src.begin (), src.end ());
145+ return std::make_pair (move_into (src.begin (), src.begin () + size1), move_into (src.begin () + size1, src.end ()));
141146 }
142147
143148 // all other overlap types will have to copy some part of the data, but if
144149 // we copy after sorting it will likely have high cache locality, so we sort
145150 // each copy separately
146- auto Copy = Src;
147- std::sort (Src.begin (), Src.end ());
148- std::sort (Copy.begin (), Copy.end ());
149-
150- switch (Pos) {
151- case OverlapPosition::Nowhere:
152- break ;
153-
154- case OverlapPosition::Front:
155- return std::make_pair (
156- moveInto (Src.begin (), Src.begin () + Size1),
157- moveInto (Copy.begin (), Copy.begin () + Size2));
158-
159- case OverlapPosition::Back:
160- return std::make_pair (
161- moveInto (Src.begin () + (Src.size () - Size1), Src.end ()),
162- moveInto (Copy.begin () + (Copy.size () - Size2), Copy.end ()));
163-
164- case OverlapPosition::Interlaced:
165- const auto Stride1 = Size1 < Size2 ? Size2/Size1 : 1 ;
166- const auto Stride2 = Size2 < Size1 ? Size1/Size2 : 1 ;
167- return std::make_pair (
168- moveInto (StridedFwdIt (Src.begin (), Stride1), StridedFwdIt (Src.end (), Stride1)),
169- moveInto (StridedFwdIt (Copy.begin (), Stride2), StridedFwdIt (Copy.end (), Stride2)));
151+ auto copy = src;
152+ std::sort (src.begin (), src.end ());
153+ std::sort (copy.begin (), copy.end ());
154+
155+ switch (pos) {
156+ case OverlapPosition::None:
157+ break ;
158+
159+ case OverlapPosition::Front:
160+ return std::make_pair (move_into (src.begin (), src.begin () + size1), move_into (copy.begin (), copy.begin () + size2));
161+
162+ case OverlapPosition::Back:
163+ return std::make_pair (move_into (src.begin () + (src.size () - size1), src.end ()),
164+ move_into (copy.begin () + (copy.size () - size2), copy.end ()));
165+
166+ case OverlapPosition::Interlaced:
167+ const auto stride1 = size1 < size2 ? size2 / size1 : 1 ;
168+ const auto stride2 = size2 < size1 ? size1 / size2 : 1 ;
169+ return std::make_pair (move_into (StridedFwdIt (src.begin (), stride1), StridedFwdIt (src.end (), stride1)),
170+ move_into (StridedFwdIt (copy.begin (), stride2), StridedFwdIt (copy.end (), stride2)));
170171 }
171172 abort ();
172173 return std::pair<Container, Container>();
173174}
174175
175-
176176template <class ValueType , class Container , class Overlap >
177177struct SetIntersection {
178178 using ContainerType = typename Container::template type<Value<ValueType>>;
179- size_t Size1 ;
180- size_t Size2 ;
179+ size_t size1_ ;
180+ size_t size2_ ;
181181
182- SetIntersection (size_t M , size_t N ) : Size1(M ), Size2(N ) {}
182+ SetIntersection (size_t size1 , size_t size2 ) : size1_(size1 ), size2_(size2 ) {}
183183
184184 void run (benchmark::State& state) const {
185185 state.PauseTiming ();
186- auto Input = genCacheUnfriendlyData<ContainerType>(Size1, Size2 , Overlap ());
187- std::vector<Value<ValueType>> out (std::min (Size1, Size2 ));
186+ auto input = genCacheUnfriendlyData<ContainerType>(size1_, size2_ , Overlap ());
187+ std::vector<Value<ValueType>> out (std::min (size1_, size2_ ));
188188
189189 size_t cmp;
190- auto trackingLess = [&cmp](const Value<ValueType>& lhs, const Value<ValueType>& rhs) {
191- ++cmp;
192- return std::less<Value<ValueType>>{}(lhs, rhs);
190+ auto tracking_less = [&cmp](const Value<ValueType>& lhs, const Value<ValueType>& rhs) {
191+ ++cmp;
192+ return std::less<Value<ValueType>>{}(lhs, rhs);
193193 };
194194
195- const auto BatchSize = std::max (size_t {16 }, (2 * TestSetElements) / (Size1+Size2 ));
195+ const auto BATCH_SIZE = std::max (size_t {16 }, (2 * TestSetElements) / (size1_ + size2_ ));
196196 state.ResumeTiming ();
197197
198198 for (const auto & _ : state) {
199- while (state.KeepRunningBatch (BatchSize )) {
200- for (unsigned i= 0 ; i<BatchSize ; ++i) {
201- const auto & [C1, C2 ] = Input ;
202- auto outIter = std::set_intersection (C1 .begin (), C1 .end (), C2 .begin (), C2 .end (), out.begin (), trackingLess );
203- benchmark::DoNotOptimize (outIter );
199+ while (state.KeepRunningBatch (BATCH_SIZE )) {
200+ for (unsigned i = 0 ; i < BATCH_SIZE ; ++i) {
201+ const auto & [c1, c2 ] = input ;
202+ auto res = std::set_intersection (c1 .begin (), c1 .end (), c2 .begin (), c2 .end (), out.begin (), tracking_less );
203+ benchmark::DoNotOptimize (res );
204204 state.counters [" Comparisons" ] = cmp;
205205 }
206206 }
207207 }
208208 }
209209
210210 std::string name () const {
211- return std::string (" SetIntersection" ) + Overlap::name () + ' _' + Container::Name +
212- ValueType::name () + ' _ ' + std::to_string (Size1 ) + ' _' + std::to_string (Size2 );
211+ return std::string (" SetIntersection" ) + Overlap::name () + ' _' + Container::Name + ValueType::name () + ' _ ' +
212+ std::to_string (size1_ ) + ' _' + std::to_string (size2_ );
213213 }
214214};
215215
216216} // namespace
217217
218- int main (int argc, char ** argv) {/* */
218+ int main (int argc, char ** argv) { /* */
219219 benchmark::Initialize (&argc, argv);
220220 if (benchmark::ReportUnrecognizedArguments (argc, argv))
221221 return 1 ;
222- makeCartesianProductBenchmark<SetIntersection, AllValueTypes, AllContainerTypes, AllOverlapPositions>(Quantities, Quantities);
222+ makeCartesianProductBenchmark<SetIntersection, AllValueTypes, AllContainerTypes, AllOverlapPositions>(
223+ Quantities, Quantities);
223224 benchmark::RunSpecifiedBenchmarks ();
224225}
0 commit comments