Optimize pool resource allocation
A recent change caused a performance regression. This restores the previous performance and adds a performance test. * scripts/check_performance: Allow tests to choose a -std flag. * src/c++17/memory_resource.cc (bitset::get_first_unset()): Use local variables of the right types. Call update_next_word() unconditionally. * testsuite/20_util/unsynchronized_pool_resource/cons.cc: New test. * testsuite/performance/20_util/memory_resource/pools.cc: New test. * testsuite/util/testsuite_performance.h (time_counter): Allow timer to be restarted. From-SVN: r266164
This commit is contained in:
parent
aeb2b1f7fb
commit
a15032ee7b
|
@ -1,5 +1,13 @@
|
||||||
2018-11-15 Jonathan Wakely <jwakely@redhat.com>
|
2018-11-15 Jonathan Wakely <jwakely@redhat.com>
|
||||||
|
|
||||||
|
* scripts/check_performance: Allow tests to choose a -std flag.
|
||||||
|
* src/c++17/memory_resource.cc (bitset::get_first_unset()): Use local
|
||||||
|
variables of the right types. Call update_next_word() unconditionally.
|
||||||
|
* testsuite/20_util/unsynchronized_pool_resource/cons.cc: New test.
|
||||||
|
* testsuite/performance/20_util/memory_resource/pools.cc: New test.
|
||||||
|
* testsuite/util/testsuite_performance.h (time_counter): Allow
|
||||||
|
timer to be restarted.
|
||||||
|
|
||||||
* testsuite/20_util/unsynchronized_pool_resource/allocate.cc: Fix
|
* testsuite/20_util/unsynchronized_pool_resource/allocate.cc: Fix
|
||||||
test for 32-bit targets. Test additional allocation sizes.
|
test for 32-bit targets. Test additional allocation sizes.
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,8 @@ do
|
||||||
TESTNAME=$SRC_DIR/testsuite/$NAME
|
TESTNAME=$SRC_DIR/testsuite/$NAME
|
||||||
FILE_NAME="`basename $NAME`"
|
FILE_NAME="`basename $NAME`"
|
||||||
FILE_NAME="`echo $FILE_NAME | sed 's/.cc//g'`"
|
FILE_NAME="`echo $FILE_NAME | sed 's/.cc//g'`"
|
||||||
|
ORIG_CXX="$CXX"
|
||||||
|
CXX="$CXX `sed -n 's/.* STD=/-std=/p' $TESTNAME`"
|
||||||
|
|
||||||
# TEST_S == single thread
|
# TEST_S == single thread
|
||||||
# TEST_B == do both single and multi-thread
|
# TEST_B == do both single and multi-thread
|
||||||
|
@ -90,6 +92,7 @@ do
|
||||||
mv tmp.$FILE_NAME $FILE_NAME.xml
|
mv tmp.$FILE_NAME $FILE_NAME.xml
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
CXX="$ORIG_CXX"
|
||||||
done
|
done
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
@ -335,17 +335,16 @@ namespace pmr
|
||||||
|
|
||||||
size_type get_first_unset() noexcept
|
size_type get_first_unset() noexcept
|
||||||
{
|
{
|
||||||
if (_M_next_word < nwords())
|
const size_type wd = _M_next_word;
|
||||||
|
if (wd < nwords())
|
||||||
{
|
{
|
||||||
const size_type n = std::__countr_one(_M_words[_M_next_word]);
|
const size_type n = std::__countr_one(_M_words[wd]);
|
||||||
if (n < bits_per_word)
|
if (n < bits_per_word)
|
||||||
{
|
{
|
||||||
const word bit = word(1) << n;
|
const word bit = word(1) << n;
|
||||||
_M_words[_M_next_word] |= bit;
|
_M_words[wd] |= bit;
|
||||||
const size_t res = (_M_next_word * bits_per_word) + n;
|
update_next_word();
|
||||||
if (n == (bits_per_word - 1))
|
return (wd * bits_per_word) + n;
|
||||||
update_next_word();
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return size_type(-1);
|
return size_type(-1);
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright (C) 2018 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++17" }
|
||||||
|
// { dg-do run { target c++17 } }
|
||||||
|
|
||||||
|
#include <memory_resource>
|
||||||
|
#include <testsuite_hooks.h>
|
||||||
|
#include <testsuite_allocator.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
test01()
|
||||||
|
{
|
||||||
|
__gnu_test::memory_resource test_mr1, test_mr2;
|
||||||
|
__gnu_test::default_resource_mgr mgr(&test_mr1);
|
||||||
|
|
||||||
|
const std::pmr::pool_options opts{1, 2};
|
||||||
|
using std::pmr::unsynchronized_pool_resource;
|
||||||
|
|
||||||
|
unsynchronized_pool_resource p1 = {opts, &test_mr2};
|
||||||
|
VERIFY( p1.upstream_resource() == &test_mr2 );
|
||||||
|
unsynchronized_pool_resource p2;
|
||||||
|
VERIFY( p2.upstream_resource() == std::pmr::get_default_resource() );
|
||||||
|
unsynchronized_pool_resource p3{&test_mr2};
|
||||||
|
VERIFY( p3.upstream_resource() == &test_mr2 );
|
||||||
|
unsynchronized_pool_resource p4{opts};
|
||||||
|
VERIFY( p4.upstream_resource() == std::pmr::get_default_resource() );
|
||||||
|
|
||||||
|
static_assert(!std::is_copy_constructible_v<unsynchronized_pool_resource>);
|
||||||
|
static_assert(!std::is_copy_assignable_v<unsynchronized_pool_resource>);
|
||||||
|
static_assert(std::is_destructible_v<unsynchronized_pool_resource>);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test02()
|
||||||
|
{
|
||||||
|
__gnu_test::memory_resource test_mr1, test_mr2;
|
||||||
|
__gnu_test::default_resource_mgr mgr(&test_mr1);
|
||||||
|
|
||||||
|
const std::pmr::pool_options opts{1, 2};
|
||||||
|
|
||||||
|
struct derived : std::pmr::unsynchronized_pool_resource
|
||||||
|
{
|
||||||
|
using unsynchronized_pool_resource::unsynchronized_pool_resource;
|
||||||
|
};
|
||||||
|
|
||||||
|
derived p1 = {opts, &test_mr2};
|
||||||
|
VERIFY( p1.upstream_resource() == &test_mr2 );
|
||||||
|
derived p2;
|
||||||
|
VERIFY( p2.upstream_resource() == std::pmr::get_default_resource() );
|
||||||
|
derived p3{&test_mr2};
|
||||||
|
VERIFY( p3.upstream_resource() == &test_mr2 );
|
||||||
|
derived p4{opts};
|
||||||
|
VERIFY( p4.upstream_resource() == std::pmr::get_default_resource() );
|
||||||
|
|
||||||
|
static_assert(!std::is_copy_constructible_v<derived>);
|
||||||
|
static_assert(!std::is_copy_assignable_v<derived>);
|
||||||
|
static_assert(std::is_destructible_v<derived>);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
test01();
|
||||||
|
test02();
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
// 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/>.
|
||||||
|
|
||||||
|
// Override the -std flag in the check_performance script: STD=gnu++17
|
||||||
|
|
||||||
|
#include <memory_resource>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <testsuite_performance.h>
|
||||||
|
|
||||||
|
struct size16 { char c[16]; };
|
||||||
|
struct size32 { char c[32]; };
|
||||||
|
struct size64 { char c[64]; };
|
||||||
|
struct size128 { char c[128]; };
|
||||||
|
|
||||||
|
// Insert and remove elements of various sizes in std::list containers.
|
||||||
|
// If report=true the function will measure and report the total performance
|
||||||
|
// including the time taken to destroy the lists and deallocate everything.
|
||||||
|
// If dest=false the function will measure and report the performance of
|
||||||
|
// insert/remove operations only, not the destruction of the lists.
|
||||||
|
void
|
||||||
|
populate_lists(std::pmr::memory_resource* r, std::string name, bool dest,
|
||||||
|
int kmax = 100)
|
||||||
|
{
|
||||||
|
name += " std::list push/pop";
|
||||||
|
if (dest)
|
||||||
|
name += "/destroy";
|
||||||
|
|
||||||
|
std::pmr::list<int> l4(r);
|
||||||
|
std::pmr::list<size16> l16(r);
|
||||||
|
std::pmr::list<size32> l32(r);
|
||||||
|
std::pmr::list<size64> l64(r);
|
||||||
|
std::pmr::list<size128> l128(r);
|
||||||
|
|
||||||
|
using namespace __gnu_test;
|
||||||
|
time_counter time;
|
||||||
|
resource_counter resource;
|
||||||
|
start_counters(time, resource);
|
||||||
|
|
||||||
|
const int imax = 1000;
|
||||||
|
const int jmax = 100;
|
||||||
|
for (int k = 0; k < kmax; ++k)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < imax; ++i)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < jmax; ++j)
|
||||||
|
{
|
||||||
|
l4.emplace_back();
|
||||||
|
l16.emplace_back();
|
||||||
|
l32.emplace_back();
|
||||||
|
l64.emplace_back();
|
||||||
|
l128.emplace_back();
|
||||||
|
}
|
||||||
|
l4.pop_front();
|
||||||
|
l16.pop_front();
|
||||||
|
l32.pop_front();
|
||||||
|
l64.pop_front();
|
||||||
|
l128.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dest)
|
||||||
|
time.stop();
|
||||||
|
|
||||||
|
// Deallocate everything:
|
||||||
|
l4.clear();
|
||||||
|
l16.clear();
|
||||||
|
l32.clear();
|
||||||
|
l64.clear();
|
||||||
|
l128.clear();
|
||||||
|
|
||||||
|
if (!dest)
|
||||||
|
time.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_counters(time, resource);
|
||||||
|
|
||||||
|
report_performance(__FILE__, name.c_str(), time, resource);
|
||||||
|
clear_counters(time, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::pmr::memory_resource* newdel = std::pmr::new_delete_resource();
|
||||||
|
std::pmr::unsynchronized_pool_resource pool;
|
||||||
|
|
||||||
|
for (auto b : { false, true })
|
||||||
|
{
|
||||||
|
// Start with an empty set of pools:
|
||||||
|
pool.release();
|
||||||
|
|
||||||
|
populate_lists(newdel, "new_delete 1", b);
|
||||||
|
populate_lists(newdel, "new_delete 2", b);
|
||||||
|
populate_lists(newdel, "new_delete 3", b);
|
||||||
|
|
||||||
|
populate_lists(&pool, "unsync pool 1", b);
|
||||||
|
// Destroy pools and start fresh:
|
||||||
|
pool.release();
|
||||||
|
populate_lists(&pool, "unsync pool 2", b);
|
||||||
|
// Do not destroy pools, reuse allocated memory:
|
||||||
|
populate_lists(&pool, "unsync pool 3", b);
|
||||||
|
}
|
||||||
|
}
|
|
@ -79,10 +79,12 @@ namespace __gnu_test
|
||||||
clock_t elapsed_end;
|
clock_t elapsed_end;
|
||||||
tms tms_begin;
|
tms tms_begin;
|
||||||
tms tms_end;
|
tms tms_end;
|
||||||
|
std::size_t splits[3];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit
|
explicit
|
||||||
time_counter() : elapsed_begin(), elapsed_end(), tms_begin(), tms_end()
|
time_counter()
|
||||||
|
: elapsed_begin(), elapsed_end(), tms_begin(), tms_end(), splits()
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -92,6 +94,7 @@ namespace __gnu_test
|
||||||
elapsed_end = clock_t();
|
elapsed_end = clock_t();
|
||||||
tms_begin = tms();
|
tms_begin = tms();
|
||||||
tms_end = tms();
|
tms_end = tms();
|
||||||
|
splits[0] = splits[1] = splits[2] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -113,17 +116,29 @@ namespace __gnu_test
|
||||||
std::__throw_runtime_error("time_counter::stop");
|
std::__throw_runtime_error("time_counter::stop");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
restart()
|
||||||
|
{
|
||||||
|
splits[0] += (elapsed_end - elapsed_begin);
|
||||||
|
splits[1] += (tms_end.tms_utime - tms_begin.tms_utime);
|
||||||
|
splits[2] += (tms_end.tms_stime - tms_begin.tms_stime);
|
||||||
|
elapsed_begin = times(&tms_begin);
|
||||||
|
const clock_t err = clock_t(-1);
|
||||||
|
if (elapsed_begin == err)
|
||||||
|
std::__throw_runtime_error("time_counter::restart");
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
real_time() const
|
real_time() const
|
||||||
{ return elapsed_end - elapsed_begin; }
|
{ return (elapsed_end - elapsed_begin) + splits[0]; }
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
user_time() const
|
user_time() const
|
||||||
{ return tms_end.tms_utime - tms_begin.tms_utime; }
|
{ return (tms_end.tms_utime - tms_begin.tms_utime) + splits[1]; }
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
system_time() const
|
system_time() const
|
||||||
{ return tms_end.tms_stime - tms_begin.tms_stime; }
|
{ return (tms_end.tms_stime - tms_begin.tms_stime) + splits[1]; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class resource_counter
|
class resource_counter
|
||||||
|
|
Loading…
Reference in New Issue