I've got a bunch of comments below.  However, I think this test is
sufficient to demonstrate that the allocator works for the use-cases in
this series so it doesn't need to block the rest of the patches.  Over-all,
it looks really good.  Thanks!

On Mon, May 7, 2018 at 5:30 PM, Scott D Phillips <scott.d.phill...@intel.com
> wrote:

> The test pseudo-randomly makes allocations and deallocations with
> the virtual memory allocator and checks that the results are
> consistent. Specifically, we test that:
>
>  * no result from the allocator overlaps an already allocated range
>  * allocated memory fulfills the stated alignment requirement
>  * a failed result from the allocator could not have been fulfilled
>  * memory freed to the allocator can later be allocated again
>
> v2: - fix if() in test() to actually run fill()
> ---
>  configure.ac                           |   1 +
>  src/util/Makefile.am                   |   3 +-
>  src/util/meson.build                   |   1 +
>  src/util/tests/vma/Makefile.am         |  37 +++++
>  src/util/tests/vma/meson.build         |  29 ++++
>  src/util/tests/vma/vma_random_test.cpp | 239
> +++++++++++++++++++++++++++++++++
>  6 files changed, 309 insertions(+), 1 deletion(-)
>  create mode 100644 src/util/tests/vma/Makefile.am
>  create mode 100644 src/util/tests/vma/meson.build
>  create mode 100644 src/util/tests/vma/vma_random_test.cpp
>
> diff --git a/configure.ac b/configure.ac
> index c0fbfe94135..8dee15cc305 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -3111,6 +3111,7 @@ AC_CONFIG_FILES([Makefile
>                   src/util/Makefile
>                   src/util/tests/hash_table/Makefile
>                   src/util/tests/string_buffer/Makefile
> +                 src/util/tests/vma/Makefile
>                   src/util/xmlpool/Makefile
>                   src/vulkan/Makefile])
>
> diff --git a/src/util/Makefile.am b/src/util/Makefile.am
> index 07bf052175b..b51dccdadfd 100644
> --- a/src/util/Makefile.am
> +++ b/src/util/Makefile.am
> @@ -22,7 +22,8 @@
>  SUBDIRS = . \
>         xmlpool \
>         tests/hash_table \
> -       tests/string_buffer
> +       tests/string_buffer \
> +       tests/vma
>
>  include Makefile.sources
>
> diff --git a/src/util/meson.build b/src/util/meson.build
> index 14660e0fa0c..c777984e28d 100644
> --- a/src/util/meson.build
> +++ b/src/util/meson.build
> @@ -159,4 +159,5 @@ if with_tests
>
>    subdir('tests/hash_table')
>    subdir('tests/string_buffer')
> +  subdir('tests/vma')
>  endif
> diff --git a/src/util/tests/vma/Makefile.am b/src/util/tests/vma/Makefile.
> am
> new file mode 100644
> index 00000000000..1c4dd302bfa
> --- /dev/null
> +++ b/src/util/tests/vma/Makefile.am
> @@ -0,0 +1,37 @@
> +# Copyright © 2018 Intel Corporation
> +#
> +#  Permission is hereby granted, free of charge, to any person obtaining a
> +#  copy of this software and associated documentation files (the
> "Software"),
> +#  to deal in the Software without restriction, including without
> limitation
> +#  the rights to use, copy, modify, merge, publish, distribute,
> sublicense,
> +#  and/or sell copies of the Software, and to permit persons to whom the
> +#  Software is furnished to do so, subject to the following conditions:
> +#
> +#  The above copyright notice and this permission notice (including the
> next
> +#  paragraph) shall be included in all copies or substantial portions of
> the
> +#  Software.
> +#
> +#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR
> +#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY,
> +#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT
> SHALL
> +#  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> OTHER
> +#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> +#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> DEALINGS
> +#  IN THE SOFTWARE.
> +
> +AM_CPPFLAGS = \
> +       -I$(top_srcdir)/include \
> +       -I$(top_srcdir)/src/util \
> +       $(DEFINES)
> +
> +TESTS = vma_random_test
> +
> +check_PROGRAMS = $(TESTS)
> +
> +vma_random_test_SOURCES = \
> +       vma_random_test.cpp
> +
> +vma_random_test_LDADD = \
> +       $(top_builddir)/src/util/libmesautil.la
> +
> +EXTRA_DIST = meson.build
> diff --git a/src/util/tests/vma/meson.build b/src/util/tests/vma/meson.
> build
> new file mode 100644
> index 00000000000..53562db312b
> --- /dev/null
> +++ b/src/util/tests/vma/meson.build
> @@ -0,0 +1,29 @@
> +# Copyright © 2018 Intel Corporation
> +
> +# Permission is hereby granted, free of charge, to any person obtaining a
> copy
> +# of this software and associated documentation files (the "Software"),
> to deal
> +# in the Software without restriction, including without limitation the
> rights
> +# to use, copy, modify, merge, publish, distribute, sublicense, and/or
> sell
> +# copies of the Software, and to permit persons to whom the Software is
> +# furnished to do so, subject to the following conditions:
> +
> +# The above copyright notice and this permission notice shall be included
> in
> +# all copies or substantial portions of the Software.
> +
> +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> OR
> +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> THE
> +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> FROM,
> +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> IN THE
> +# SOFTWARE.
> +
> +test(
> +  'vma_random',
> +  executable(
> +    'vma_random_test',
> +    'vma_random_test.cpp',
> +    include_directories : [inc_include, inc_util],
> +    link_with : [libmesa_util],
> +  )
> +)
> diff --git a/src/util/tests/vma/vma_random_test.cpp
> b/src/util/tests/vma/vma_random_test.cpp
> new file mode 100644
> index 00000000000..238c361a8e3
> --- /dev/null
> +++ b/src/util/tests/vma/vma_random_test.cpp
> @@ -0,0 +1,239 @@
> +/*
> + * Copyright © 2018 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the
> "Software"),
> + * to deal in the Software without restriction, including without
> limitation
> + * the rights to use, copy, modify, merge, publish, distribute,
> sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> next
> + * paragraph) shall be included in all copies or substantial portions of
> the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT
> SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +/* it is a test after all */
> +#undef NDEBUG
> +
> +#include <algorithm>
> +#include <cassert>
> +#include <climits>
> +#include <cstdint>
> +#include <cstdio>
> +#include <cstdlib>
> +#include <random>
> +#include <set>
> +#include <vector>
> +
> +#include <err.h>
> +
> +#include "vma.h"
> +
> +namespace {
> +
> +static const uint64_t PAGE_SIZE = 4096;
> +
> +struct allocation {
> +   uint64_t address;
> +   uint64_t num_pages;
> +};
> +
> +struct allocation_less {
> +   constexpr bool operator()(const allocation& lhs, const allocation&
> rhs) const
> +   {
> +      return lhs.address + lhs.num_pages * PAGE_SIZE <= rhs.address;
>

Maybe it would be better to assert that the two addresses are divisible by
PAGE_SIZE and do

lhs.address / PAGE_SIZE + lhs.num_pages <= rhs.address / PAGE_SIZE

That way you are guaranteed (unless num_pages is bogus) to never have
overflow issues.


> +   }
> +};
> +
> +constexpr uint64_t allocation_end(const allocation& a) {
> +   return a.address + PAGE_SIZE * a.num_pages;
> +}
> +
> +struct random_test {
> +   static const uint64_t MEM_START = 1 * PAGE_SIZE;
> +   static const uint64_t MEM_SIZE = 6 * (1ULL << 30 /* GiB */) -
> MEM_START;
>

It would be good to test a full 64-bit address space.  I tried to make the
allocator handle it but there are likely bugs there.


> +   static const uint64_t MEM_PAGES = (MEM_SIZE >> 12);
>

Maybe divide by PAGE_SIZE to avoid the magic constant?


> +
> +   random_test(uint_fast32_t seed)
> +      : heap_holes{allocation{MEM_START, MEM_PAGES}}, rand{seed}
> +   {
> +      util_vma_heap_init(&heap, MEM_START, MEM_SIZE);
> +   }
> +
> +   void test(unsigned long count)
> +   {
> +      std::uniform_int_distribution<> one_to_thousand(1, 1000);
> +      while (count-- > 0) {
> +         int action = one_to_thousand(rand);
> +         if (action == 1)          fill();
> +         else if (action == 2)     empty();
> +         else if (action < 374)    dealloc();
> +         else                      alloc();
> +      }
> +   }
> +
> +   bool alloc(uint64_t size_order=20, uint64_t align_order=20)
> +   {
> +      std::geometric_distribution<> dist;
> +
> +      if (align_order > 19)
> +         align_order = std::min(dist(rand), 19);
> +      uint64_t align = 1ULL << (align_order + 12);
> +
> +      if (size_order > 19)
> +         size_order = std::min(dist(rand), 19);
> +      uint64_t size_pages = 1ULL << size_order;
> +      uint64_t size = size_pages << 12;
>

Multiply by PAGE_SIZE to avoid the magic constant.


> +
> +      uint64_t addr = util_vma_heap_alloc(&heap, size, align);
> +      // printf("alloc(size:%ld, align:%ld) = %p\n", size, align, (void
> *)addr);
> +
> +      if (addr == 0) {
> +         /* assert no gaps are present in the tracker for this allocation
> */
> +         for (const auto& hole : heap_holes) {
> +            uint64_t aligned_start = (hole.address + align - 1) & ~(align
> - 1);
> +            uint64_t alignment_pages = (aligned_start - hole.address) /
> PAGE_SIZE;
> +            if (alignment_pages >= hole.num_pages)
> +               continue;
> +            assert(hole.num_pages < size_pages + alignment_pages);
> +         }
> +         return false;
> +      } else {
> +         assert(addr == (addr & ~(align - 1)));
> +         allocation a{addr, size_pages};
> +         auto i = heap_holes.find(a);
> +         assert(i != end(heap_holes));
> +         allocation hole = *i;
> +
> +         assert(hole.address <= addr);
> +         assert(hole.num_pages >= size_pages + (addr - hole.address) /
> PAGE_SIZE);
> +
> +         heap_holes.erase(i);
> +         if (hole.address < a.address) {
> +            heap_holes.emplace(allocation{hole.address, (a.address -
> hole.address) / PAGE_SIZE});
> +         }
> +         if (allocation_end(hole) > allocation_end(a)) {
> +            heap_holes.emplace(allocation{allocation_end(a),
> (allocation_end(hole) - allocation_end(a)) / PAGE_SIZE});
> +         }
> +
> +         allocations.push_back({addr, size_pages});
> +         return true;
> +      }
> +   }
> +
> +   void dealloc()
> +   {
> +      if (allocations.size() == 0)
> +         return;
> +
> +      std::uniform_int_distribution<> dist(0, allocations.size() - 1);
> +      int to_dealloc = dist(rand);
> +
> +      std::swap(*(begin(allocations) + to_dealloc),
> +                *(begin(allocations) + allocations.size() - 1));
>

Any reason why this isn't just

std::swap(allocations[to_dealloc], allocations[allocations.size() - 1]);

They're the same thing but that looks easier to read to me.


> +      allocation a = allocations.back();
> +      allocations.pop_back();
> +
> +      // printf("free(addr:%p, size:%ld)\n", (void *)a.address,
> a.num_pages << 12);
> +      util_vma_heap_free(&heap, a.address, a.num_pages << 12);
>

Multiply by PAGE_SIZE?


> +
> +      assert(heap_holes.find(a) == end(heap_holes));
> +      auto next = heap_holes.upper_bound(a);
> +      if (next != end(heap_holes)) {
> +         if (next->address == allocation_end(a)) {
> +            allocation x {a.address, a.num_pages + next->num_pages};
> +            next = heap_holes.erase(next);
> +            next = heap_holes.insert(next, x);
> +
> +            if (next != begin(heap_holes)) {
> +               auto prev = next;
> +               prev--;
> +               if (allocation_end(*prev) == next->address) {
> +                  allocation x {prev->address, prev->num_pages +
> next->num_pages};
> +                  heap_holes.erase(prev);
> +                  next = heap_holes.erase(next);
> +                  heap_holes.insert(next, x);
> +               }
> +            }
> +
> +            return;
> +         }
> +      }
> +
> +      if (next != begin(heap_holes)) {
> +         auto prev = next;
> +         prev--;
> +         if (allocation_end(*prev) == a.address) {
> +            allocation x {prev->address, prev->num_pages + a.num_pages};
> +            next = heap_holes.erase(prev);
> +            heap_holes.insert(next, x);
> +
> +            return;
> +         }
> +      }
> +
> +      heap_holes.emplace(a);
> +   }
> +
> +   void fill()
> +   {
> +      for (int sz = 19; sz >= 0; sz--) {
> +         while (alloc(sz, 0))
> +            ;
> +      }
> +      assert(heap_holes.empty());
> +   }
> +
> +   void empty()
> +   {
> +      while (allocations.size() != 0)
> +         dealloc();
> +      assert(heap_holes.size() == 1);
> +      auto& hole = *begin(heap_holes);
> +      assert(hole.address == MEM_START && hole.num_pages == MEM_PAGES);
> +   }
> +
> +   struct util_vma_heap heap;
> +   std::set<allocation, allocation_less> heap_holes;
> +   std::default_random_engine rand;
> +   std::vector<allocation> allocations;
> +};
> +
> +}
> +
> +int main(int argc, char **argv)
> +{
> +   unsigned long seed, count;
> +   if (argc == 3) {
> +      char *arg_end = NULL;
> +      seed = strtoul(argv[1], &arg_end, 0);
> +      if (!arg_end || *arg_end || seed == ULONG_MAX)
> +         errx(1, "invalid seed \"%s\"", argv[1]);
> +
> +      arg_end = NULL;
> +      count = strtoul(argv[2], &arg_end, 0);
> +      if (!arg_end || *arg_end || count == ULONG_MAX)
> +         errx(1, "invalid count \"%s\"", argv[2]);
> +   } else if (argc == 1) {
> +      /* importantly chosen prime numbers. */
> +      seed = 8675309;
> +      count = 2459;
> +   } else {
> +      errx(1, "USAGE: %s seed iter_count\n", argv[0]);
> +   }
> +
> +   random_test r{seed};
> +   r.test(count);
> +
> +   printf("ok\n");
> +   return 0;
> +}
> --
> 2.14.3
>
>
_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to