Daniel Carvalho has uploaded this change for review. ( https://gem5-review.googlesource.com/c/public/gem5/+/44111 )

Change subject: base,tests: Test base Bloom Filter
......................................................................

base,tests: Test base Bloom Filter

Add tests for the basic bloom filter.

Change-Id: Ibeed0476b7dc63795d02a524a2384d3a6bbbf28a
Signed-off-by: Daniel R. Carvalho <oda...@yahoo.com.br>
---
M src/base/filters/SConscript
A src/base/filters/base.test.cc
2 files changed, 393 insertions(+), 0 deletions(-)



diff --git a/src/base/filters/SConscript b/src/base/filters/SConscript
index d8da413..7e9f2a5 100644
--- a/src/base/filters/SConscript
+++ b/src/base/filters/SConscript
@@ -37,3 +37,5 @@
 Source('multi_bit_sel_bloom_filter.cc')
 Source('multi_bloom_filter.cc')
 Source('perfect_bloom_filter.cc')
+
+GTest('base.test', 'base.test.cc', with_tag('gem5 simobject'))
diff --git a/src/base/filters/base.test.cc b/src/base/filters/base.test.cc
new file mode 100644
index 0000000..0187ffa
--- /dev/null
+++ b/src/base/filters/base.test.cc
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 2021 Daniel R. Carvalho
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <gtest/gtest-spi.h>
+#include <gtest/gtest.h>
+
+#include <cassert>
+
+#include "base/filters/base.hh"
+
+#define DECLARE_PARAMS(name) \
+    BloomFilterBaseParams name; \
+    name.eventq_index = 0; \
+    name.size = 3; \
+    name.offset_bits = 6; \
+    name.num_bits = 1; \
+    name.threshold = 1
+
+/** Simulates basic behavior of a bloom filter. */
+class TestFilter : public BloomFilter::Base
+{
+  public:
+    using BloomFilter::Base::Base;
+
+    void
+    set(Addr addr) override
+    {
+        assert(addr < filter.size());
+        filter[addr]++;
+    }
+
+    int
+    getCount(Addr addr) const override
+    {
+        assert(addr < filter.size());
+        return filter[addr];
+    }
+};
+
+/** Test that a filter is initialized in a cleared state. */
+TEST(BloomFilterBaseTest, Construct)
+{
+    DECLARE_PARAMS(params);
+    TestFilter filter(params);
+    ASSERT_EQ(filter.getTotalCount(), 0);
+    for (int i = 0; i < params.size; i++) {
+        ASSERT_FALSE(filter.isSet(i));
+    }
+}
+
+/**
+ * Test that setting a single entry when the threshold is 1 will only set
+ * that entry, and no other entry.
+ */
+TEST(BloomFilterBaseTest, SingleIsSet)
+{
+    DECLARE_PARAMS(params);
+    TestFilter filter(params);
+    ASSERT_EQ(filter.getTotalCount(), 0);
+
+    filter.set(0);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    ASSERT_TRUE(filter.isSet(0));
+    ASSERT_FALSE(filter.isSet(1));
+    ASSERT_FALSE(filter.isSet(2));
+
+    filter.clear();
+    ASSERT_EQ(filter.getTotalCount(), 0);
+    filter.set(1);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    ASSERT_FALSE(filter.isSet(0));
+    ASSERT_TRUE(filter.isSet(1));
+    ASSERT_FALSE(filter.isSet(2));
+
+    filter.clear();
+    ASSERT_EQ(filter.getTotalCount(), 0);
+    filter.set(2);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    ASSERT_FALSE(filter.isSet(0));
+    ASSERT_FALSE(filter.isSet(1));
+    ASSERT_TRUE(filter.isSet(2));
+}
+
+/**
+ * Test that isSet works for multiple simultaneously set entries by
+ * simultaneously saturating different entries at the same time.
+ */
+TEST(BloomFilterBaseTest, MultipleIsSet)
+{
+    DECLARE_PARAMS(params);
+    TestFilter filter(params);
+    ASSERT_EQ(filter.getTotalCount(), 0);
+
+    filter.set(0);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    filter.set(1);
+    ASSERT_EQ(filter.getTotalCount(), 2);
+    ASSERT_TRUE(filter.isSet(0));
+    ASSERT_TRUE(filter.isSet(1));
+    ASSERT_FALSE(filter.isSet(2));
+
+    filter.clear();
+    ASSERT_EQ(filter.getTotalCount(), 0);
+    filter.set(1);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    filter.set(2);
+    ASSERT_EQ(filter.getTotalCount(), 2);
+    ASSERT_FALSE(filter.isSet(0));
+    ASSERT_TRUE(filter.isSet(1));
+    ASSERT_TRUE(filter.isSet(2));
+
+    filter.clear();
+    ASSERT_EQ(filter.getTotalCount(), 0);
+    filter.set(0);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    filter.set(2);
+    ASSERT_EQ(filter.getTotalCount(), 2);
+    ASSERT_TRUE(filter.isSet(0));
+    ASSERT_FALSE(filter.isSet(1));
+    ASSERT_TRUE(filter.isSet(2));
+
+    filter.clear();
+    ASSERT_EQ(filter.getTotalCount(), 0);
+    filter.set(0);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    filter.set(1);
+    ASSERT_EQ(filter.getTotalCount(), 2);
+    filter.set(2);
+    ASSERT_EQ(filter.getTotalCount(), 3);
+    ASSERT_TRUE(filter.isSet(0));
+    ASSERT_TRUE(filter.isSet(1));
+    ASSERT_TRUE(filter.isSet(2));
+}
+
+/**
+ * Test that isSet takes the threshold into consideration. This test
+ * increases the number of bits in the filter's entries to be able to
+ * raise the threshold at which an entry is considered as set.
+ */
+TEST(BloomFilterBaseTest, SingleIsSetThreshold)
+{
+    DECLARE_PARAMS(params);
+    params.num_bits = 2;
+    params.threshold = 2;
+    TestFilter filter(params);
+    ASSERT_EQ(filter.getTotalCount(), 0);
+
+    filter.set(0);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    ASSERT_FALSE(filter.isSet(0));
+    ASSERT_FALSE(filter.isSet(1));
+    ASSERT_FALSE(filter.isSet(2));
+    filter.set(0);
+    ASSERT_EQ(filter.getTotalCount(), 2);
+    ASSERT_TRUE(filter.isSet(0));
+    ASSERT_FALSE(filter.isSet(1));
+    ASSERT_FALSE(filter.isSet(2));
+
+    filter.clear();
+    filter.set(1);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    ASSERT_FALSE(filter.isSet(0));
+    ASSERT_FALSE(filter.isSet(1));
+    ASSERT_FALSE(filter.isSet(2));
+    filter.set(1);
+    ASSERT_EQ(filter.getTotalCount(), 2);
+    ASSERT_FALSE(filter.isSet(0));
+    ASSERT_TRUE(filter.isSet(1));
+    ASSERT_FALSE(filter.isSet(2));
+
+    filter.clear();
+    filter.set(2);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    ASSERT_FALSE(filter.isSet(0));
+    ASSERT_FALSE(filter.isSet(1));
+    ASSERT_FALSE(filter.isSet(2));
+    filter.set(2);
+    ASSERT_EQ(filter.getTotalCount(), 2);
+    ASSERT_FALSE(filter.isSet(0));
+    ASSERT_FALSE(filter.isSet(1));
+    ASSERT_TRUE(filter.isSet(2));
+
+    // Setting different entries once should not make any of them
+    // reach the threshold
+    filter.clear();
+    filter.set(0);
+    filter.set(1);
+    filter.set(2);
+    ASSERT_EQ(filter.getTotalCount(), 3);
+    ASSERT_FALSE(filter.isSet(0));
+    ASSERT_FALSE(filter.isSet(1));
+    ASSERT_FALSE(filter.isSet(2));
+}
+
+/** Test that merging two empty bloom filters results in an empty filter. */
+TEST(BloomFilterBaseTest, MergeBothEmpty)
+{
+    DECLARE_PARAMS(params);
+    TestFilter filter(params);
+    TestFilter filter2(params);
+
+    filter.merge(&filter2);
+    ASSERT_EQ(filter.getTotalCount(), 0);
+    ASSERT_EQ(filter2.getTotalCount(), 0);
+}
+
+/**
+ * Test that merging a populated filter with an empty filter does not modify
+ * any of the filters.
+ */
+TEST(BloomFilterBaseTest, MergeWithEmpty)
+{
+    DECLARE_PARAMS(params);
+    TestFilter filter(params);
+    filter.set(1);
+
+    TestFilter filter2(params);
+
+    filter.merge(&filter2);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    ASSERT_TRUE(filter.isSet(1));
+    ASSERT_EQ(filter2.getTotalCount(), 0);
+}
+
+/**
+ * Test that merging an empty filter with a populated filter results in
+ * two equal filters.
+ */
+TEST(BloomFilterBaseTest, MergeWithEmpty2)
+{
+    DECLARE_PARAMS(params);
+    TestFilter filter(params);
+
+    TestFilter filter2(params);
+    filter2.set(1);
+
+    filter.merge(&filter2);
+    ASSERT_EQ(filter.getTotalCount(), 1);
+    ASSERT_TRUE(filter.isSet(1));
+    ASSERT_EQ(filter2.getTotalCount(), 1);
+    ASSERT_TRUE(filter.isSet(1));
+}
+
+/**
+ * Test merging two filters with intersecting entries. The caller is modified,
+ * but the other filter is not.
+ */
+TEST(BloomFilterBaseTest, MergeNoIntersection)
+{
+    DECLARE_PARAMS(params);
+    params.size = 10;
+
+    TestFilter filter(params);
+    filter.set(1);
+    filter.set(2);
+    filter.set(5);
+    filter.set(8);
+
+    TestFilter filter2(params);
+    filter2.set(3);
+    filter2.set(4);
+    filter2.set(9);
+
+    filter.merge(&filter2);
+    ASSERT_EQ(filter.getTotalCount(), 7);
+    ASSERT_TRUE(filter.isSet(1));
+    ASSERT_TRUE(filter.isSet(2));
+    ASSERT_TRUE(filter.isSet(3));
+    ASSERT_TRUE(filter.isSet(4));
+    ASSERT_TRUE(filter.isSet(5));
+    ASSERT_TRUE(filter.isSet(8));
+    ASSERT_TRUE(filter.isSet(9));
+    ASSERT_EQ(filter2.getTotalCount(), 3);
+    ASSERT_TRUE(filter2.isSet(3));
+    ASSERT_TRUE(filter2.isSet(4));
+    ASSERT_TRUE(filter2.isSet(9));
+}
+
+/** Test merging two filters with insersecting entries and threshold at 1. */
+TEST(BloomFilterBaseTest, MergeIntersectionThreshold1)
+{
+    DECLARE_PARAMS(params);
+    params.size = 10;
+
+    TestFilter filter(params);
+    filter.set(1);
+    filter.set(2);
+    filter.set(5);
+    filter.set(8);
+
+    TestFilter filter2(params);
+    filter2.set(3);
+    filter2.set(5);
+    filter2.set(9);
+
+    filter.merge(&filter2);
+    ASSERT_EQ(filter.getTotalCount(), 6);
+    ASSERT_TRUE(filter.isSet(1));
+    ASSERT_TRUE(filter.isSet(2));
+    ASSERT_TRUE(filter.isSet(3));
+    ASSERT_TRUE(filter.isSet(5));
+    ASSERT_TRUE(filter.isSet(8));
+    ASSERT_TRUE(filter.isSet(9));
+    ASSERT_EQ(filter2.getTotalCount(), 3);
+    ASSERT_TRUE(filter2.isSet(3));
+    ASSERT_TRUE(filter2.isSet(5));
+    ASSERT_TRUE(filter2.isSet(9));
+}
+
+/**
+ * Test merging two filters with insersecting entries and threshold at 2.
+ * One entry is populated so that it only reaches the threshold after merging.
+ * One entry is populated so that when merged it will become saturated.
+ */
+TEST(BloomFilterBaseTest, MergeIntersectionThreshold2)
+{
+    DECLARE_PARAMS(params);
+    params.size = 10;
+    params.num_bits = 2;
+    params.threshold = 2;
+
+    TestFilter filter(params);
+    filter.set(1);
+    filter.set(2);
+    filter.set(5);
+    filter.set(5);
+    filter.set(8);
+
+    TestFilter filter2(params);
+    filter2.set(2);
+    filter2.set(5);
+    filter2.set(5);
+    filter2.set(5);
+    filter2.set(9);
+
+    filter.merge(&filter2);
+    // 1 one, 2 twos, 3 fives (saturated), 1 eight, 1 nine
+    ASSERT_EQ(filter.getTotalCount(), 8);
+    ASSERT_TRUE(filter.isSet(2));
+    ASSERT_TRUE(filter.isSet(5));
+    ASSERT_EQ(filter2.getTotalCount(), 5);
+    ASSERT_FALSE(filter2.isSet(2));
+    ASSERT_TRUE(filter2.isSet(5));
+}
+
+/** Test that trying to merge filters of different sizes fails. */
+TEST(BloomFilterBaseDeathTest, MergeDifferent)
+{
+#ifdef NDEBUG
+    GTEST_SKIP() << "Skipping as assertions are "
+        "stripped out of fast builds";
+#endif
+    DECLARE_PARAMS(params);
+    TestFilter filter(params);
+
+    BloomFilterBaseParams params2;
+    params2.eventq_index = params.eventq_index;
+    params2.size = params.size + 1;
+    params2.offset_bits = params.offset_bits;
+    params2.num_bits = params.num_bits;
+    params2.threshold = params.threshold;
+    TestFilter filter2(params2);
+
+    ASSERT_DEATH(filter.merge(&filter2), "");
+}
+

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/44111
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: Ibeed0476b7dc63795d02a524a2384d3a6bbbf28a
Gerrit-Change-Number: 44111
Gerrit-PatchSet: 1
Gerrit-Owner: Daniel Carvalho <oda...@yahoo.com.br>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list -- gem5-dev@gem5.org
To unsubscribe send an email to gem5-dev-le...@gem5.org
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s

Reply via email to