Optimize pool resource allocation
authorJonathan Wakely <jwakely@redhat.com>
Thu, 15 Nov 2018 00:04:19 +0000 (00:04 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 15 Nov 2018 00:04:19 +0000 (00:04 +0000)
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

libstdc++-v3/ChangeLog
libstdc++-v3/scripts/check_performance
libstdc++-v3/src/c++17/memory_resource.cc
libstdc++-v3/testsuite/20_util/unsynchronized_pool_resource/cons.cc [new file with mode: 0644]
libstdc++-v3/testsuite/performance/20_util/memory_resource/pools.cc [new file with mode: 0644]
libstdc++-v3/testsuite/util/testsuite_performance.h

index 5262ef547824e5301be5e60bb84cf6ac1ff18c6f..85e79eae8b2e60e939bb1cb09ce2b2eb9adbe8e8 100644 (file)
@@ -1,5 +1,13 @@
 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
        test for 32-bit targets. Test additional allocation sizes.
 
index d196355bd44deeba2a28421cb42f68c3820e6c1b..3fa927480c9ea714270ac9ac4c12fe79712b049d 100755 (executable)
@@ -44,6 +44,8 @@ do
   TESTNAME=$SRC_DIR/testsuite/$NAME
   FILE_NAME="`basename $NAME`"
   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_B == do both single and multi-thread
@@ -90,6 +92,7 @@ do
        mv tmp.$FILE_NAME $FILE_NAME.xml
     fi
   fi
+  CXX="$ORIG_CXX"
 done
 
 exit 0
index 605bdd53950b05c93f677618018d474288d59e92..79c1665146d6eebe6c971e9fd98fbd2ffee442f4 100644 (file)
@@ -335,17 +335,16 @@ namespace pmr
 
     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)
            {
              const word bit = word(1) << n;
-             _M_words[_M_next_word] |= bit;
-             const size_t res = (_M_next_word * bits_per_word) + n;
-             if (n == (bits_per_word - 1))
-               update_next_word();
-             return res;
+             _M_words[wd] |= bit;
+             update_next_word();
+             return (wd * bits_per_word) + n;
            }
        }
       return size_type(-1);
diff --git a/libstdc++-v3/testsuite/20_util/unsynchronized_pool_resource/cons.cc b/libstdc++-v3/testsuite/20_util/unsynchronized_pool_resource/cons.cc
new file mode 100644 (file)
index 0000000..14519ba
--- /dev/null
@@ -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();
+}
diff --git a/libstdc++-v3/testsuite/performance/20_util/memory_resource/pools.cc b/libstdc++-v3/testsuite/performance/20_util/memory_resource/pools.cc
new file mode 100644 (file)
index 0000000..77f37fe
--- /dev/null
@@ -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);
+  }
+}
index 3dfd471221dd6e697e3a3e763fbc6a4ed034f060..38ec35d4c456506b3f1c333a58caec7ab90e66f8 100644 (file)
@@ -79,10 +79,12 @@ namespace __gnu_test
     clock_t    elapsed_end;
     tms                tms_begin;
     tms                tms_end;
+    std::size_t splits[3];
 
   public:
     explicit
-    time_counter() : elapsed_begin(), elapsed_end(), tms_begin(), tms_end()
+    time_counter()
+    : elapsed_begin(), elapsed_end(), tms_begin(), tms_end(), splits()
     { }
 
     void
@@ -92,6 +94,7 @@ namespace __gnu_test
       elapsed_end = clock_t();
       tms_begin = tms();
       tms_end = tms();
+      splits[0] = splits[1] = splits[2] = 0;
     }
 
     void
@@ -113,17 +116,29 @@ namespace __gnu_test
        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
     real_time() const
-    { return elapsed_end - elapsed_begin; }
+    { return (elapsed_end - elapsed_begin) + splits[0]; }
 
     std::size_t
     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
     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