From a79df54f2c802f7cb5125d0fe29035707224a1a5 Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Thu, 14 Mar 2019 18:59:08 -0500
Subject: [PATCH] Make leading bits used by mem pool configurable, document

---
 doc/tools.rst        | 25 ++++++++++++++++++++++++-
 src/mempool.hpp      | 36 +++++++++++++++++++++---------------
 src/wrap_mempool.cpp |  9 ++++++---
 3 files changed, 51 insertions(+), 19 deletions(-)

diff --git a/doc/tools.rst b/doc/tools.rst
index ade730a1..24353514 100644
--- a/doc/tools.rst
+++ b/doc/tools.rst
@@ -68,7 +68,7 @@ not complicated::
 
         Allocate a :class:`pyopencl.Buffer` of the given *size*.
 
-.. class:: MemoryPool(allocator)
+.. class:: MemoryPool(allocator[, leading_bits_in_bin_id])
 
     A memory pool for OpenCL device memory. *allocator* must be an instance of
     one of the above classes, and should be an :class:`ImmediateAllocator`.
@@ -76,6 +76,29 @@ not complicated::
     by the allocator immediately, and not in the OpenCL-typical
     deferred manner.
 
+    .. note::
+
+        The current implementation of the memory pool will retain allocated
+        memory after it is returned by the application and keep it in a bin
+        identified by the leading *leading_bits_in_bin_id* bits of the
+        allocation size. To ensure that allocations within each bin are
+        interchangeable, allocation sizes are rounded up to the largest size
+        that shares the leading bits of the requested allocation size.
+
+        The current default value of *leading_bits_in_bin_id* is
+        four, but this may change in future versions and is not
+        guaranteed.
+
+        *leading_bits_in_bin_id* must be passed by keyword,
+        and its role is purely advisory. It is not guaranteed
+        that future versions of the pool will use the
+        same allocation scheme and/or honor *leading_bits_in_bin_id*.
+
+    .. versionchanged:: 2019.1
+
+        Current bin allocation behavior documented, *leading_bits_in_bin_id*
+        added.
+
     .. attribute:: held_blocks
 
         The number of unused blocks being held by this pool.
diff --git a/src/mempool.hpp b/src/mempool.hpp
index 3469af8c..3491c69d 100644
--- a/src/mempool.hpp
+++ b/src/mempool.hpp
@@ -91,11 +91,13 @@ namespace PYGPU_PACKAGE
       bool m_stop_holding;
       int m_trace;
 
+      unsigned m_leading_bits_in_bin_id;
+
     public:
-      memory_pool(Allocator const &alloc=Allocator())
+      memory_pool(Allocator const &alloc=Allocator(), unsigned leading_bits_in_bin_id=4)
         : m_allocator(alloc.copy()),
         m_held_blocks(0), m_active_blocks(0), m_stop_holding(false),
-        m_trace(false)
+        m_trace(false), m_leading_bits_in_bin_id(leading_bits_in_bin_id)
       {
         if (m_allocator->is_deferred())
         {
@@ -109,17 +111,21 @@ namespace PYGPU_PACKAGE
       virtual ~memory_pool()
       { free_held(); }
 
-      static const unsigned mantissa_bits = 2;
-      static const unsigned mantissa_mask = (1 << mantissa_bits) - 1;
+    private:
+      unsigned mantissa_mask() const
+      {
+        return (1 << m_leading_bits_in_bin_id) - 1;
+      }
 
-      static bin_nr_t bin_number(size_type size)
+    public:
+      bin_nr_t bin_number(size_type size)
       {
         signed l = bitlog2(size);
-        size_type shifted = signed_right_shift(size, l-signed(mantissa_bits));
-        if (size && (shifted & (1 << mantissa_bits)) == 0)
+        size_type shifted = signed_right_shift(size, l-signed(m_leading_bits_in_bin_id));
+        if (size && (shifted & (1 << m_leading_bits_in_bin_id)) == 0)
           throw std::runtime_error("memory_pool::bin_number: bitlog2 fault");
-        size_type chopped = shifted & mantissa_mask;
-        return l << mantissa_bits | chopped;
+        size_type chopped = shifted & mantissa_mask();
+        return l << m_leading_bits_in_bin_id | chopped;
       }
 
       void set_trace(bool flag)
@@ -130,19 +136,19 @@ namespace PYGPU_PACKAGE
           --m_trace;
       }
 
-      static size_type alloc_size(bin_nr_t bin)
+      size_type alloc_size(bin_nr_t bin)
       {
-        bin_nr_t exponent = bin >> mantissa_bits;
-        bin_nr_t mantissa = bin & mantissa_mask;
+        bin_nr_t exponent = bin >> m_leading_bits_in_bin_id;
+        bin_nr_t mantissa = bin & mantissa_mask();
 
         size_type ones = signed_left_shift(1,
-            signed(exponent)-signed(mantissa_bits)
+            signed(exponent)-signed(m_leading_bits_in_bin_id)
             );
         if (ones) ones -= 1;
 
         size_type head = signed_left_shift(
-           (1<<mantissa_bits) | mantissa,
-            signed(exponent)-signed(mantissa_bits));
+           (1<<m_leading_bits_in_bin_id) | mantissa,
+            signed(exponent)-signed(m_leading_bits_in_bin_id));
         if (ones & head)
           throw std::runtime_error("memory_pool::alloc_size: bit-counting fault");
         return head | ones;
diff --git a/src/wrap_mempool.cpp b/src/wrap_mempool.cpp
index a6db0924..31cae81e 100644
--- a/src/wrap_mempool.cpp
+++ b/src/wrap_mempool.cpp
@@ -238,8 +238,8 @@ namespace
     wrapper
       .def_property_readonly("held_blocks", &cls::held_blocks)
       .def_property_readonly("active_blocks", &cls::active_blocks)
-      .DEF_SIMPLE_STATIC_METHOD(bin_number)
-      .DEF_SIMPLE_STATIC_METHOD(alloc_size)
+      .DEF_SIMPLE_METHOD(bin_number)
+      .DEF_SIMPLE_METHOD(alloc_size)
       .DEF_SIMPLE_METHOD(free_held)
       .DEF_SIMPLE_METHOD(stop_holding)
       ;
@@ -293,7 +293,10 @@ void pyopencl_expose_mempool(py::module &m)
       cls, /* boost::noncopyable, */
       std::shared_ptr<cls>> wrapper( m, "MemoryPool");
     wrapper
-      .def(py::init<cl_allocator_base const &>())
+      .def(py::init<cl_allocator_base const &, unsigned>(),
+          py::arg("allocator"),
+          py::arg("leading_bits_in_bin_id")=4
+          )
       .def("allocate", device_pool_allocate)
       .def("__call__", device_pool_allocate)
       // undoc for now
-- 
GitLab