public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Thomas Schwinge <thomas@codesourcery.com>
To: Richard Biener <richard.guenther@gmail.com>,
	Martin Sebor <msebor@gmail.com>, <gcc-patches@gcc.gnu.org>
Cc: David Malcolm <dmalcolm@redhat.com>,
	Jonathan Wakely <jwakely.gcc@gmail.com>, <gcc@gcc.gnu.org>
Subject: Add more self-tests for 'hash_map' with Value type with non-trivial constructor/destructor (was: Expensive selftests)
Date: Wed, 18 Aug 2021 13:34:28 +0200	[thread overview]
Message-ID: <87v943nfzv.fsf@euler.schwinge.homeip.net> (raw)
In-Reply-To: <CAFiYyc3wRHF97mhRG372EKpbBNL7B6pYGKyvw40wzKooJjiydg@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 2601 bytes --]

Hi!

On 2021-08-17T10:57:36+0200, Richard Biener <richard.guenther@gmail.com> wrote:
> On Tue, Aug 17, 2021 at 8:40 AM Thomas Schwinge <thomas@codesourcery.com> wrote:
>> On 2021-08-16T14:10:00-0600, Martin Sebor <msebor@gmail.com> wrote:
>> > On 8/16/21 6:44 AM, Thomas Schwinge wrote:
>> >> [...], to document the current behavior, I propose to
>> >> "Add more self-tests for 'hash_map' with Value type with non-trivial
>> >> constructor/destructor", see attached.  OK to push to master branch?
>> >> (Also cherry-pick into release branches, eventually?)

>> > Adding more tests sounds like an excellent idea.  I'm not sure about
>> > the idea of adding loopy selftests that iterate as many times as in
>> > the patch (looks like 1234 times two?)
>>
>> Correct, and I agree it's a sensible concern, generally.
>>
>> The current 1234 times two iterations is really arbitrary (should
>> document that in the test case), just so that we trigger a few hash table
>> expansions.
>
> You could lower N_init (the default init is just 13!),
> even with just 128 inserted elements you'll trigger
> expansions to 31, 61 and 127 elements.

> I think the test is OK but it's also reasonable to lower
> the '1234' times and add a comment as to the count should
> trigger hashtable expansions "a few times".

Did that as follows:

    /* Verify basic construction and destruction of Value objects.  */
    {
      /* Configure, arbitrary.  */
      const size_t N_init = 0;
      const int N_elem = 28;

..., and:

    /* Verify aspects of 'hash_table::expand'.  */

    static void
    test_map_of_type_with_ctor_and_dtor_expand (bool remove_some_inline)
    {
      /* Configure, so that hash table expansion triggers a few times.  */
      const size_t N_init = 0;
      const int N_elem = 70;
      size_t expand_c_expected = 4;
    [...]
          if (expand)
        {
          ++expand_c;
    [...]
      ASSERT_EQ (expand_c, expand_c_expected);

Given that it's all deterministic (as far as I can tell...), we may
verify an exact number of hash table expansions.

Pushed "Add more self-tests for 'hash_map' with Value type with
non-trivial constructor/destructor" to master branch in commit
e4f16e9f357a38ec702fb69a0ffab9d292a6af9b, see attached.


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-more-self-tests-for-hash_map-with-Value-type-wit.patch --]
[-- Type: text/x-diff, Size: 5252 bytes --]

From e4f16e9f357a38ec702fb69a0ffab9d292a6af9b Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Fri, 13 Aug 2021 17:53:12 +0200
Subject: [PATCH] Add more self-tests for 'hash_map' with Value type with
 non-trivial constructor/destructor

... to document the current behavior.

	gcc/
	* hash-map-tests.c (test_map_of_type_with_ctor_and_dtor): Extend.
	(test_map_of_type_with_ctor_and_dtor_expand): Add function.
	(hash_map_tests_c_tests): Call it.
---
 gcc/hash-map-tests.c | 152 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 152 insertions(+)

diff --git a/gcc/hash-map-tests.c b/gcc/hash-map-tests.c
index 5b6b192cd28..257f2be0c26 100644
--- a/gcc/hash-map-tests.c
+++ b/gcc/hash-map-tests.c
@@ -278,6 +278,156 @@ test_map_of_type_with_ctor_and_dtor ()
 
     ASSERT_TRUE (val_t::ndefault + val_t::ncopy == val_t::ndtor);
   }
+
+
+  /* Verify basic construction and destruction of Value objects.  */
+  {
+    /* Configure, arbitrary.  */
+    const size_t N_init = 0;
+    const int N_elem = 28;
+
+    void *a[N_elem];
+    for (size_t i = 0; i < N_elem; ++i)
+      a[i] = &a[i];
+
+    val_t::ndefault = 0;
+    val_t::ncopy = 0;
+    val_t::nassign = 0;
+    val_t::ndtor = 0;
+    Map m (N_init);
+    ASSERT_EQ (val_t::ndefault
+	       + val_t::ncopy
+	       + val_t::nassign
+	       + val_t::ndtor, 0);
+
+    for (int i = 0; i < N_elem; ++i)
+      {
+	m.get_or_insert (a[i]);
+	ASSERT_EQ (val_t::ndefault, 1 + i);
+	ASSERT_EQ (val_t::ncopy, 0);
+	ASSERT_EQ (val_t::nassign, 0);
+	ASSERT_EQ (val_t::ndtor, i);
+
+	m.remove (a[i]);
+	ASSERT_EQ (val_t::ndefault, 1 + i);
+	ASSERT_EQ (val_t::ncopy, 0);
+	ASSERT_EQ (val_t::nassign, 0);
+	ASSERT_EQ (val_t::ndtor, 1 + i);
+      }
+  }
+}
+
+/* Verify aspects of 'hash_table::expand'.  */
+
+static void
+test_map_of_type_with_ctor_and_dtor_expand (bool remove_some_inline)
+{
+  /* Configure, so that hash table expansion triggers a few times.  */
+  const size_t N_init = 0;
+  const int N_elem = 70;
+  size_t expand_c_expected = 4;
+  size_t expand_c = 0;
+
+  void *a[N_elem];
+  for (size_t i = 0; i < N_elem; ++i)
+    a[i] = &a[i];
+
+  typedef hash_map <void *, val_t> Map;
+
+  /* Note that we are starting with a fresh 'Map'.  Even if an existing one has
+     been cleared out completely, there remain 'deleted' elements, and these
+     would disturb the following logic, where we don't have access to the
+     actual 'm_n_deleted' value.  */
+  size_t m_n_deleted = 0;
+
+  val_t::ndefault = 0;
+  val_t::ncopy = 0;
+  val_t::nassign = 0;
+  val_t::ndtor = 0;
+  Map m (N_init);
+
+  /* In the following, in particular related to 'expand', we're adapting from
+     the internal logic of 'hash_table', glossing over "some details" not
+     relevant for this testing here.  */
+
+  /* Per 'hash_table::hash_table'.  */
+  size_t m_size;
+  {
+    unsigned int size_prime_index_ = hash_table_higher_prime_index (N_init);
+    m_size = prime_tab[size_prime_index_].prime;
+  }
+
+  int n_expand_moved = 0;
+
+  for (int i = 0; i < N_elem; ++i)
+    {
+      size_t elts = m.elements ();
+
+      /* Per 'hash_table::find_slot_with_hash'.  */
+      size_t m_n_elements = elts + m_n_deleted;
+      bool expand = m_size * 3 <= m_n_elements * 4;
+
+      m.get_or_insert (a[i]);
+      if (expand)
+	{
+	  ++expand_c;
+
+	  /* Per 'hash_table::expand'.  */
+	  {
+	    unsigned int nindex = hash_table_higher_prime_index (elts * 2);
+	    m_size = prime_tab[nindex].prime;
+	  }
+	  m_n_deleted = 0;
+
+	  /* All non-deleted elements have been moved.  */
+	  n_expand_moved += i;
+	  if (remove_some_inline)
+	    n_expand_moved -= (i + 2) / 3;
+	}
+
+      ASSERT_EQ (val_t::ndefault, 1 + i);
+      ASSERT_EQ (val_t::ncopy, n_expand_moved);
+      ASSERT_EQ (val_t::nassign, 0);
+      if (remove_some_inline)
+	ASSERT_EQ (val_t::ndtor, (i + 2) / 3);
+      else
+	ASSERT_EQ (val_t::ndtor, 0);
+
+      /* Remove some inline.  This never triggers an 'expand' here, but via
+	 'm_n_deleted' does influence any following one.  */
+      if (remove_some_inline
+	  && !(i % 3))
+	{
+	  m.remove (a[i]);
+	  /* Per 'hash_table::remove_elt_with_hash'.  */
+	  m_n_deleted++;
+
+	  ASSERT_EQ (val_t::ndefault, 1 + i);
+	  ASSERT_EQ (val_t::ncopy, n_expand_moved);
+	  ASSERT_EQ (val_t::nassign, 0);
+	  ASSERT_EQ (val_t::ndtor, 1 + (i + 2) / 3);
+	}
+    }
+  ASSERT_EQ (expand_c, expand_c_expected);
+
+  int ndefault = val_t::ndefault;
+  int ncopy = val_t::ncopy;
+  int nassign = val_t::nassign;
+  int ndtor = val_t::ndtor;
+
+  for (int i = 0; i < N_elem; ++i)
+    {
+      if (remove_some_inline
+	  && !(i % 3))
+	continue;
+
+      m.remove (a[i]);
+      ++ndtor;
+      ASSERT_EQ (val_t::ndefault, ndefault);
+      ASSERT_EQ (val_t::ncopy, ncopy);
+      ASSERT_EQ (val_t::nassign, nassign);
+      ASSERT_EQ (val_t::ndtor, ndtor);
+    }
 }
 
 /* Test calling empty on a hash_map that has a key type with non-zero
@@ -309,6 +459,8 @@ hash_map_tests_c_tests ()
   test_map_of_strings_to_int ();
   test_map_of_int_to_strings ();
   test_map_of_type_with_ctor_and_dtor ();
+  test_map_of_type_with_ctor_and_dtor_expand (false);
+  test_map_of_type_with_ctor_and_dtor_expand (true);
   test_nonzero_empty_key ();
 }
 
-- 
2.30.2


  reply	other threads:[~2021-08-18 11:34 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <87r1f6qzmx.fsf@euler.schwinge.homeip.net>
     [not found] ` <af8fa221-b555-c192-bd99-6eb73db3935f@gmail.com>
2021-08-16 12:44   ` 'hash_map<tree, hash_map<tree, tree>>' Thomas Schwinge
2021-08-16 20:10     ` Martin Sebor
2021-08-17  6:40       ` Expensive selftests (was: 'hash_map<tree, hash_map<tree, tree>>') Thomas Schwinge
2021-08-17  8:57         ` Richard Biener
2021-08-18 11:34           ` Thomas Schwinge [this message]
2021-08-18 13:35           ` David Edelsohn
2021-08-18 15:34             ` Make 'gcc/hash-map-tests.c:test_map_of_type_with_ctor_and_dtor_expand' work on 32-bit architectures [PR101959] Thomas Schwinge
2021-08-18 16:12               ` Richard Biener
2021-08-17 15:01         ` Expensive selftests Martin Sebor
2021-08-30 10:46       ` Fix 'hash_table::expand' to destruct stale Value objects (was: 'hash_map<tree, hash_map<tree, tree>>') Thomas Schwinge
2021-09-02  1:31         ` Fix 'hash_table::expand' to destruct stale Value objects Martin Sebor
2021-09-10  8:00           ` [PING] " Thomas Schwinge
2021-09-17 11:17             ` [PING^2] " Thomas Schwinge
2021-09-17 12:08               ` Richard Biener
2021-09-17 12:39                 ` Jonathan Wakely
2021-09-17 13:03                   ` Richard Biener
2021-09-17 15:52                     ` Thomas Schwinge
2021-09-17 19:08                       ` Jonathan Wakely
2021-09-20  9:11                       ` Richard Biener

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87v943nfzv.fsf@euler.schwinge.homeip.net \
    --to=thomas@codesourcery.com \
    --cc=dmalcolm@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=gcc@gcc.gnu.org \
    --cc=jwakely.gcc@gmail.com \
    --cc=msebor@gmail.com \
    --cc=richard.guenther@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).