blob: ec4787336546dfb5e8f3b5b1cd1854dbac363d63 [file] [log] [blame]
// Copyright (C) 2013-2014 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;
}