mirror of
https://github.com/autc04/Retro68.git
synced 2024-09-06 22:54:33 +00:00
337 lines
9.7 KiB
C++
337 lines
9.7 KiB
C++
// Copyright (C) 2013-2016 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License along
|
|
// with this library; see the file COPYING3. If not see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
// { dg-options "-std=gnu++11" }
|
|
|
|
#include <testsuite_performance.h>
|
|
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <unordered_set>
|
|
|
|
namespace
|
|
{
|
|
const int sz = 2000000;
|
|
const std::string pattern = "test string #";
|
|
const int nb_copies = 100;
|
|
|
|
// Special std::string hasher based on std::hash<std::string> but not tag as
|
|
// slow so that hash code is not cached. It is easier to show impact of
|
|
// hinting in this context.
|
|
struct hasher
|
|
{
|
|
std::size_t
|
|
operator()(const std::string& str) const noexcept
|
|
{
|
|
//std::istringstream istr(str.substr(pattern.size()));
|
|
//std::size_t str_index;
|
|
//istr >> str_index;
|
|
//return str_index;
|
|
std::hash<std::string> std_hasher;
|
|
return std_hasher(str);
|
|
}
|
|
};
|
|
|
|
using ums_t = std::unordered_multiset<std::string, hasher>;
|
|
|
|
void
|
|
insert_with_perfect_hint1(const std::vector<std::string>& strs,
|
|
ums_t& ms)
|
|
{
|
|
std::vector<typename ums_t::iterator> hints;
|
|
hints.reserve(strs.size());
|
|
for (auto str : strs)
|
|
hints.push_back(ms.insert(str));
|
|
|
|
for (int j = 1; j != nb_copies; ++j)
|
|
for (std::size_t i = 0; i != strs.size(); ++i)
|
|
ms.insert(hints[i], strs[i]);
|
|
}
|
|
|
|
void
|
|
insert_with_perfect_hint2(const std::vector<std::string>& strs,
|
|
ums_t& ms)
|
|
{
|
|
std::vector<typename ums_t::iterator> hints;
|
|
hints.reserve(strs.size());
|
|
for (auto str : strs)
|
|
hints.push_back(ms.insert(str));
|
|
|
|
for (std::size_t i = 0; i != strs.size(); ++i)
|
|
for (int j = 1; j != nb_copies; ++j)
|
|
ms.insert(hints[i], strs[i]);
|
|
}
|
|
|
|
// Always insert with the result of the previous insertion. The result of
|
|
// the previous insertion will never be followed by an equivalent node
|
|
// resulting in a re-computation of its hash code which is expensive.
|
|
void
|
|
insert_with_good_hint(const std::vector<std::string>& strs,
|
|
ums_t& ms)
|
|
{
|
|
std::vector<typename ums_t::iterator> hints;
|
|
hints.reserve(strs.size());
|
|
for (auto str : strs)
|
|
hints.push_back(ms.insert(str));
|
|
|
|
for (int j = 1; j != nb_copies; ++j)
|
|
for (std::size_t i = 0; i != strs.size(); ++i)
|
|
hints[i] = ms.insert(hints[i], strs[i]);
|
|
}
|
|
|
|
// Note that the following use case is particularly bad especially compared to
|
|
// the solution without hint because without hint the first insertion will put
|
|
// it first in the bucket and following insertions will detect it and insert
|
|
// just before. By giving a hint insertion will be done just after forcing to
|
|
// check if it has no impact on the following bucket.
|
|
void
|
|
insert_with_bad_hint(const std::vector<std::string>& strs,
|
|
ums_t& ms)
|
|
{
|
|
std::vector<typename ums_t::iterator> hints;
|
|
hints.reserve(strs.size());
|
|
for (auto str : strs)
|
|
hints.push_back(ms.insert(str));
|
|
|
|
for (std::size_t i = 0; i != strs.size(); ++i)
|
|
for (int j = 1; j != nb_copies; ++j)
|
|
hints[i] = ms.insert(hints[i], strs[i]);
|
|
}
|
|
|
|
void
|
|
insert_without_hint1(const std::vector<std::string>& strs,
|
|
ums_t& ms)
|
|
{
|
|
std::vector<typename ums_t::iterator> hints;
|
|
hints.reserve(strs.size());
|
|
for (auto str : strs)
|
|
hints.push_back(ms.insert(str));
|
|
|
|
for (int j = 1; j != nb_copies; ++j)
|
|
for (std::size_t i = 0; i != strs.size(); ++i)
|
|
hints[i] = ms.insert(strs[i]);
|
|
}
|
|
|
|
// This version is the best one if you insert all equivalent elements at the
|
|
// same time. It demonstrates that most of the time it is better not to give
|
|
// any hint unless you have written a benchmark for your application showing
|
|
// that it does have a positive effect.
|
|
void
|
|
insert_without_hint2(const std::vector<std::string>& strs,
|
|
ums_t& ms)
|
|
{
|
|
std::vector<typename ums_t::iterator> hints;
|
|
hints.reserve(strs.size());
|
|
for (auto str : strs)
|
|
hints.push_back(ms.insert(str));
|
|
|
|
for (std::size_t i = 0; i != strs.size(); ++i)
|
|
for (int j = 1; j != nb_copies; ++j)
|
|
hints[i] = ms.insert(strs[i]);
|
|
}
|
|
|
|
void
|
|
insert_with_any_hint1(const std::vector<std::string>& strs,
|
|
ums_t& ms)
|
|
{
|
|
std::vector<typename ums_t::iterator> hints;
|
|
hints.reserve(strs.size());
|
|
for (auto str : strs)
|
|
hints.push_back(ms.insert(ms.begin(), str));
|
|
|
|
std::size_t offset = strs.size() / 2;
|
|
for (int j = 1; j != nb_copies; ++j)
|
|
for (std::size_t i = 0; i != strs.size(); ++i)
|
|
{
|
|
ms.insert(hints[(i + offset) % hints.size()], strs[i]);
|
|
++offset;
|
|
}
|
|
}
|
|
|
|
void
|
|
insert_with_any_hint2(const std::vector<std::string>& strs,
|
|
ums_t& ms)
|
|
{
|
|
std::vector<typename ums_t::iterator> hints;
|
|
hints.reserve(strs.size());
|
|
for (auto str : strs)
|
|
hints.push_back(ms.insert(ms.begin(), str));
|
|
|
|
std::size_t offset = strs.size() / 2;
|
|
for (std::size_t i = 0; i != strs.size(); ++i)
|
|
for (int j = 1; j != nb_copies; ++j)
|
|
{
|
|
ms.insert(hints[(i + offset) % hints.size()], strs[i]);
|
|
++offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
using namespace __gnu_test;
|
|
|
|
const int nb_iter = 10;
|
|
|
|
std::vector<std::string> strs;
|
|
strs.reserve(sz / nb_copies);
|
|
|
|
for (int i = 0; i != sz / nb_copies; ++i)
|
|
{
|
|
std::ostringstream osstr;
|
|
osstr << pattern << i;
|
|
strs.push_back(osstr.str());
|
|
}
|
|
|
|
ums_t ms;
|
|
// Use a large load factor to make the context ideal for using hint because we
|
|
// will have many elements per bucket.
|
|
ms.max_load_factor(10.0f);
|
|
ms.reserve(sz);
|
|
|
|
// Warm up.
|
|
{
|
|
for (auto str : strs)
|
|
for (int j = 0; j != nb_copies; ++j)
|
|
ms.insert(str);
|
|
}
|
|
|
|
time_counter time_no_hint1, time_any_hint1, time_bad_hint, time_perfect_hint1;
|
|
time_counter time_no_hint2, time_any_hint2, time_good_hint, time_perfect_hint2;
|
|
resource_counter resource_no_hint1, resource_any_hint1, resource_bad_hint,
|
|
resource_perfect_hint1;
|
|
resource_counter resource_no_hint2, resource_any_hint2, resource_good_hint,
|
|
resource_perfect_hint2;
|
|
|
|
for (int i = 0; i != nb_iter; ++i)
|
|
{
|
|
// Bad hint
|
|
{
|
|
ms.clear();
|
|
start_counters(time_bad_hint, resource_bad_hint);
|
|
insert_with_bad_hint(strs, ms);
|
|
stop_counters(time_bad_hint, resource_bad_hint);
|
|
}
|
|
|
|
// No hint
|
|
{
|
|
ms.clear();
|
|
start_counters(time_no_hint1, resource_no_hint1);
|
|
insert_without_hint1(strs, ms);
|
|
stop_counters(time_no_hint1, resource_no_hint1);
|
|
}
|
|
|
|
// Any hint
|
|
{
|
|
ms.clear();
|
|
start_counters(time_any_hint1, resource_any_hint1);
|
|
insert_with_any_hint1(strs, ms);
|
|
stop_counters(time_any_hint1, resource_any_hint1);
|
|
}
|
|
|
|
// Good hint
|
|
{
|
|
ms.clear();
|
|
start_counters(time_good_hint, resource_good_hint);
|
|
insert_with_good_hint(strs, ms);
|
|
stop_counters(time_good_hint, resource_good_hint);
|
|
}
|
|
|
|
// No hint
|
|
{
|
|
ms.clear();
|
|
start_counters(time_no_hint2, resource_no_hint2);
|
|
insert_without_hint2(strs, ms);
|
|
stop_counters(time_no_hint2, resource_no_hint2);
|
|
}
|
|
|
|
// Perfect hint
|
|
{
|
|
ms.clear();
|
|
start_counters(time_perfect_hint2, resource_perfect_hint2);
|
|
insert_with_perfect_hint2(strs, ms);
|
|
stop_counters(time_perfect_hint2, resource_perfect_hint2);
|
|
}
|
|
|
|
// Any hint
|
|
{
|
|
ms.clear();
|
|
start_counters(time_any_hint2, resource_any_hint2);
|
|
insert_with_any_hint2(strs, ms);
|
|
stop_counters(time_any_hint2, resource_any_hint2);
|
|
}
|
|
|
|
// Perfect hint
|
|
{
|
|
ms.clear();
|
|
start_counters(time_perfect_hint1, resource_perfect_hint1);
|
|
insert_with_perfect_hint1(strs, ms);
|
|
stop_counters(time_perfect_hint1, resource_perfect_hint1);
|
|
}
|
|
}
|
|
|
|
std::ostringstream ostr;
|
|
ostr << "unordered_set " << nb_copies << " X " << sz / nb_copies
|
|
<< " insertions w/o hint";
|
|
report_performance(__FILE__, ostr.str().c_str(),
|
|
time_no_hint1, resource_no_hint1);
|
|
|
|
ostr.str("");
|
|
ostr << "unordered_set " << nb_copies << " X " << sz / nb_copies
|
|
<< " insertions with any hint";
|
|
report_performance(__FILE__, ostr.str().c_str(),
|
|
time_any_hint1, resource_any_hint1);
|
|
|
|
ostr.str("");
|
|
ostr << "unordered_set " << nb_copies << " X " << sz / nb_copies
|
|
<< " insertions with good hint";
|
|
report_performance(__FILE__, ostr.str().c_str(),
|
|
time_good_hint, resource_good_hint);
|
|
|
|
ostr.str("");
|
|
ostr << "unordered_set " << nb_copies << " X " << sz / nb_copies
|
|
<< " insertions with perfect hint";
|
|
report_performance(__FILE__, ostr.str().c_str(),
|
|
time_perfect_hint1, resource_perfect_hint1);
|
|
|
|
ostr.str("");
|
|
ostr << "unordered_set " << sz / nb_copies << " X " << nb_copies
|
|
<< " insertions w/o hint";
|
|
report_performance(__FILE__, ostr.str().c_str(),
|
|
time_no_hint2, resource_no_hint2);
|
|
|
|
ostr.str("");
|
|
ostr << "unordered_set " << sz / nb_copies << " X " << nb_copies
|
|
<< " insertions with any hint";
|
|
report_performance(__FILE__, ostr.str().c_str(),
|
|
time_any_hint2, resource_any_hint2);
|
|
|
|
ostr.str("");
|
|
ostr << "unordered_set " << sz / nb_copies << " X " << nb_copies
|
|
<< " insertions with bad hint";
|
|
report_performance(__FILE__, ostr.str().c_str(),
|
|
time_bad_hint, resource_bad_hint);
|
|
|
|
ostr.str("");
|
|
ostr << "unordered_set " << sz / nb_copies << " X " << nb_copies
|
|
<< " insertions with perfect hint";
|
|
report_performance(__FILE__, ostr.str().c_str(),
|
|
time_perfect_hint2, resource_perfect_hint2);
|
|
return 0;
|
|
}
|