Topic: The Concept of Memory Allocator


Author: Mingxin Wang <wmx16835vv@163.com>
Date: Sun, 16 Sep 2018 22:38:38 -0700 (PDT)
Raw View
------=_Part_1383_1794769167.1537162718678
Content-Type: multipart/alternative;
 boundary="----=_Part_1384_1830568197.1537162718678"

------=_Part_1384_1830568197.1537162718678
Content-Type: text/plain; charset="UTF-8"

I would like to propose a new concept of "Memory Allocator", which was
previously discussed as "Memory Resource"
<https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/iw3JRVF8EPk>
..

*Motivation*

Runtime memory allocation is a common requirement in C++ projects. The
concept of "Memory Allocator" is a new way to abstract this requirement in
order to simplify the code requiring runtime memory allocation, especially
in type-erased contexts.

In C++98, the concept of "Allocator" was introduced, and was widely adopted
in STL. However, since the concept of "Allocator" has too many
customization points and is type-specific, it becomes difficult to reuse
this concept in type-erased components. For example, the class template
std::function used to have a constructor that allow customized allocator
types, just like other STL containers does, but the constructor was
eventually removed in C++17 because "the semantics are unclear, and there
are technical issues with storing an allocator in a type-erased context and
then recovering that allocator later for any allocations needed during copy
assignment" [P0302R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0302r1.html>].
With the concept of "Memory Allocator", it will be much easier to add
customization points in type-erased components, like the "PFA" - a generic,
extendable and efficient solution for polymorphic programming [P0957R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0957r1.pdf>].

In C++17, we have the concept of "Memory Resources". However, it is defined
with "intrusive polymorphism", and any implementation of the concept shall
be derived from the base class std::pmr::memory_resource with virtual
functions, which will introduce unnecessary runtime space occupation and
overhead when the "memory resource" itself is not required to be
polymorphic. Besides, since the size and alignment of the type to allocate
are known at compile-time, it will be better for performance to pass them
to the corresponding functions as compile-time constants rather than
runtime variables. Considering usability and performance, not only does the
"Memory Allocator" not require the implementations to be polymorphic
themselves, but also allow passing size and alignment with compile-time
constants.

Therefore, if the concept of "Memory Allocator" is introduced in the
standard, it will become possible for us to introduce reliable and
extendable memory allocation mechanism to type-erased components, including
the class template std::function, the class std::any and the "PFA" [P0957R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0957r1.pdf>].

*Technical Specifications*

*1. The BasicMemoryAllocator requirements*

A type MA meets the BasicMemoryAllocator requirements of specific
std::integral_constant of SIZE and ALIGN if the following expressions are
well-formed and have the specified semantics (ma denotes a value of type
MA, s donates a value of std::integral_constant<std::size_t, SIZE>, a
donates a value of std::integral_constant<std::size_t, ALIGN>, p donates
the returned value).

ma.allocate(s, a)
Effects: Allocates a continuous block of memory with at least specific size
of SIZE and alignment of ALIGN. The allocated memory will be available
until a subsequent corresponding call to ma.deallocate(p, s, a).
Return type: void*
Returns: A pointer pointing to the first byte of the memory being allocated.

ma.deallocate(p, s, a)
Requires: p shall have been returned from a prior call to ma.allocate(s,
a), and the storage at p shall not yet have been deallocated.
Effects: Deallocates the memory pointed by p allocated before.

*2. The MemoryAllocator requirements*

A type MA meets the MemoryAllocator requirements of specific
std::integral_constant of SIZE and ALIGN if it meets the
BasicMemoryAllocator requirements of std::integral_constant of SIZE and
ALIGN, and the following expressions are well-formed and have the specified
semantics (ma denotes a value of type MA, s donates a value of
std::integral_constant<std::size_t, SIZE>, a donates a value of
std::integral_constant<std::size_t, ALIGN>, p donates the returned value, n
donates the length of the array of std::size_t).

ma.allocate(n, s, a)
Effects: Allocates a continuous block of memory with at least specific size
of (n * SIZE) and alignment of ALIGN. The allocated memory will be
available until a subsequent corresponding call to ma.deallocate(p, n, s,
a).
Return type: void*
Returns: A pointer pointing to the first byte of the memory being allocated.

ma.deallocate(p, n, s, a)
Requires: p shall have been returned from a prior call to ma.allocate(n, s,
a), and the storage at p shall not yet have been deallocated.
Effects: Deallocates the memory pointed by p allocated before.

*3. Class memory_allocator*

namespace std {

class memory_allocator {
 public:
  template <size_t SIZE, size_t ALIGN>
  void* allocate(integral_constant<size_t, SIZE>,
      integral_constant<size_t, ALIGN>);

  template <size_t SIZE, size_t ALIGN>
  void deallocate(void* p, integral_constant<size_t, SIZE>,
      integral_constant<size_t, ALIGN>);

  template <size_t SIZE, size_t ALIGN>
  void* allocate(size_t n, integral_constant<size_t, SIZE>,
      integral_constant<size_t, ALIGN>);

  template <size_t SIZE, size_t ALIGN>
  void deallocate(void* p, size_t, integral_constant<size_t, SIZE>,
      integral_constant<size_t, ALIGN>);
};

}

The class memory_allocator meets the MemoryAllocator requirements for any
positive std::integral_constant of SIZE and ALIGN, and is expected to be
included in header <memory>.

*Known Use Case*

The BasicMemoryAllocator requirements and the class memory_allocator is
used as an experimental extension
<https://github.com/wmx16835/cpp_pfa/blob/master/ext/memory_allocator.h>
for the PFA [P0957R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0957r1.pdf>], and
is used in the constructor of the built-in addresser of value semantics.

I am looking forward to your valuable comments!

Mingxin Wang

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/d6633a1d-bd01-4d34-be40-7c041a3e1119%40isocpp.org.

------=_Part_1384_1830568197.1537162718678
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div>I would like to propose a new concept of &quot;Memory=
 Allocator&quot;, which was previously discussed as <a href=3D"https://grou=
ps.google.com/a/isocpp.org/forum/#!topic/std-proposals/iw3JRVF8EPk">&quot;M=
emory Resource&quot;</a>.<br></div><div><br></div><div><font size=3D"4"><b>=
Motivation</b></font><br></div><div><br></div><div>Runtime memory allocatio=
n is a common requirement in C++ projects. The concept of &quot;Memory Allo=
cator&quot; is a new way to abstract this requirement in order to simplify =
the code requiring runtime memory allocation, especially in type-erased con=
texts.</div><div><br></div><div>In C++98, the concept of &quot;Allocator&qu=
ot; was introduced, and was widely adopted in STL. However, since the conce=
pt of &quot;Allocator&quot; has too many customization points and is type-s=
pecific, it becomes difficult to reuse this concept in type-erased componen=
ts. For example, the class template std::function used to have a constructo=
r that allow customized allocator types, just like other STL containers doe=
s, but the constructor was eventually removed in C++17 because &quot;the se=
mantics are unclear, and there are technical issues with storing an allocat=
or in a type-erased context and then recovering that allocator later for an=
y allocations needed during copy assignment&quot; [<a href=3D"http://www.op=
en-std.org/jtc1/sc22/wg21/docs/papers/2016/p0302r1.html">P0302R1</a>]. With=
 the concept of &quot;Memory Allocator&quot;, it will be much easier to add=
 customization points in type-erased components, like the &quot;PFA&quot; -=
 a generic, extendable and efficient solution for polymorphic programming [=
<a href=3D"http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0957r1.=
pdf">P0957R1</a>].</div><div><br></div><div>In C++17, we have the concept o=
f &quot;Memory Resources&quot;. However, it is defined with &quot;intrusive=
 polymorphism&quot;, and any implementation of the concept shall be derived=
 from the base class std::pmr::memory_resource with virtual functions, whic=
h will introduce unnecessary runtime space occupation and overhead when the=
 &quot;memory resource&quot; itself is not required to be polymorphic. Besi=
des, since the size and alignment of the type to allocate are known at comp=
ile-time, it will be better for performance to pass them to the correspondi=
ng functions as compile-time constants rather than runtime variables. Consi=
dering usability and performance, not only does the &quot;Memory Allocator&=
quot; not require the implementations to be polymorphic themselves, but als=
o allow passing size and alignment with compile-time constants.</div><div><=
br></div><div>Therefore, if the concept of &quot;Memory Allocator&quot; is =
introduced in the standard, it will become possible for us to introduce rel=
iable and extendable memory allocation mechanism to type-erased components,=
 including the class template std::function, the class std::any and the &qu=
ot;PFA&quot; [<a href=3D"http://www.open-std.org/jtc1/sc22/wg21/docs/papers=
/2018/p0957r1.pdf">P0957R1</a>].</div><div><br></div><div><font size=3D"4">=
<b>Technical Specifications</b></font></div><div><br></div><div><i>1. The B=
asicMemoryAllocator requirements</i></div><div><br></div><div>A type MA mee=
ts the BasicMemoryAllocator requirements of specific std::integral_constant=
 of SIZE and ALIGN if the following expressions are well-formed and have th=
e specified semantics (ma denotes a value of type MA, s donates a value of =
std::integral_constant&lt;std::size_t, SIZE&gt;, a donates a value of std::=
integral_constant&lt;std::size_t, ALIGN&gt;, p donates the returned value).=
</div><div><br></div><div>ma.allocate(s, a)</div><div><span style=3D"white-=
space: pre;"> </span>Effects: Allocates a continuous block of memory with a=
t least specific size of SIZE and alignment of ALIGN. The allocated memory =
will be available until a subsequent corresponding call to ma.deallocate(p,=
 s, a).</div><div><span style=3D"white-space: pre;"> </span>Return type: vo=
id*</div><div><span style=3D"white-space: pre;"> </span>Returns: A pointer =
pointing to the first byte of the memory being allocated.</div><div><br></d=
iv><div>ma.deallocate(p, s, a)</div><div><span style=3D"white-space: pre;">=
 </span>Requires: p shall have been returned from a prior call to ma.alloca=
te(s, a), and the storage at p shall not yet have been deallocated.</div><d=
iv><span style=3D"white-space: pre;"> </span>Effects: Deallocates the memor=
y pointed by p allocated before.</div><div><br></div><div><i>2. The MemoryA=
llocator requirements</i></div><div><br></div><div>A type MA meets the Memo=
ryAllocator requirements of specific std::integral_constant of SIZE and ALI=
GN if it meets the BasicMemoryAllocator requirements of std::integral_const=
ant of SIZE and ALIGN, and the following expressions are well-formed and ha=
ve the specified semantics (ma denotes a value of type MA, s donates a valu=
e of std::integral_constant&lt;std::size_t, SIZE&gt;, a donates a value of =
std::integral_constant&lt;std::size_t, ALIGN&gt;, p donates the returned va=
lue, n donates the length of the array of std::size_t).</div><div><br></div=
><div>ma.allocate(n, s, a)</div><div><span style=3D"white-space: pre;"> </s=
pan>Effects: Allocates a continuous block of memory with at least specific =
size of (n * SIZE) and alignment of ALIGN. The allocated memory will be ava=
ilable until a subsequent corresponding call to ma.deallocate(p, n, s, a).<=
/div><div><span style=3D"white-space: pre;"> </span>Return type: void*</div=
><div><span style=3D"white-space: pre;"> </span>Returns: A pointer pointing=
 to the first byte of the memory being allocated.</div><div><br></div><div>=
ma.deallocate(p, n, s, a)</div><div><span style=3D"white-space: pre;"> </sp=
an>Requires: p shall have been returned from a prior call to ma.allocate(n,=
 s, a), and the storage at p shall not yet have been deallocated.</div><div=
><span style=3D"white-space: pre;"> </span>Effects: Deallocates the memory =
pointed by p allocated before.</div><div><br></div><div><i>3. Class memory_=
allocator</i></div><div><br></div><div>namespace std {</div><div><br></div>=
<div>class memory_allocator {</div><div>=C2=A0public:</div><div>=C2=A0 temp=
late &lt;size_t SIZE, size_t ALIGN&gt;</div><div>=C2=A0 void* allocate(inte=
gral_constant&lt;size_t, SIZE&gt;,</div><div>=C2=A0 =C2=A0 =C2=A0 integral_=
constant&lt;size_t, ALIGN&gt;);</div><div><br></div><div>=C2=A0 template &l=
t;size_t SIZE, size_t ALIGN&gt;</div><div>=C2=A0 void deallocate(void* p, i=
ntegral_constant&lt;size_t, SIZE&gt;,</div><div>=C2=A0 =C2=A0 =C2=A0 integr=
al_constant&lt;size_t, ALIGN&gt;);</div><div><br></div><div>=C2=A0 template=
 &lt;size_t SIZE, size_t ALIGN&gt;</div><div>=C2=A0 void* allocate(size_t n=
, integral_constant&lt;size_t, SIZE&gt;,</div><div>=C2=A0 =C2=A0 =C2=A0 int=
egral_constant&lt;size_t, ALIGN&gt;);</div><div><br></div><div>=C2=A0 templ=
ate &lt;size_t SIZE, size_t ALIGN&gt;</div><div>=C2=A0 void deallocate(void=
* p, size_t, integral_constant&lt;size_t, SIZE&gt;,</div><div>=C2=A0 =C2=A0=
 =C2=A0 integral_constant&lt;size_t, ALIGN&gt;);</div><div>};</div><div><br=
></div><div>}</div><div><br></div><div>The class memory_allocator meets the=
 MemoryAllocator requirements for any positive std::integral_constant of SI=
ZE and ALIGN, and is expected to be included in header &lt;memory&gt;.</div=
><div><br></div><div><font size=3D"4"><b>Known Use Case</b></font></div><di=
v><br></div><div>The BasicMemoryAllocator requirements and the class memory=
_allocator is used as an <a href=3D"https://github.com/wmx16835/cpp_pfa/blo=
b/master/ext/memory_allocator.h">experimental extension</a> for the PFA [<a=
 href=3D"http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0957r1.pd=
f">P0957R1</a>], and is used in the constructor of the built-in addresser o=
f value semantics.</div><div><br></div><div>I am looking forward to your va=
luable comments!</div><div><br></div><div>Mingxin Wang</div></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/d6633a1d-bd01-4d34-be40-7c041a3e1119%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/d6633a1d-bd01-4d34-be40-7c041a3e1119=
%40isocpp.org</a>.<br />

------=_Part_1384_1830568197.1537162718678--

------=_Part_1383_1794769167.1537162718678--

.