Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
d1573452
Commit
d1573452
authored
Jan 13, 2021
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement monotonic_buffer_resource
parent
5820a182
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
457 additions
and
0 deletions
+457
-0
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+2
-0
libcaf_core/caf/detail/monotonic_buffer_resource.hpp
libcaf_core/caf/detail/monotonic_buffer_resource.hpp
+168
-0
libcaf_core/src/detail/monotonic_buffer_resource.cpp
libcaf_core/src/detail/monotonic_buffer_resource.cpp
+165
-0
libcaf_core/test/detail/monotonic_buffer_resource.cpp
libcaf_core/test/detail/monotonic_buffer_resource.cpp
+122
-0
No files found.
libcaf_core/CMakeLists.txt
View file @
d1573452
...
@@ -113,6 +113,7 @@ caf_add_component(
...
@@ -113,6 +113,7 @@ caf_add_component(
src/detail/message_builder_element.cpp
src/detail/message_builder_element.cpp
src/detail/message_data.cpp
src/detail/message_data.cpp
src/detail/meta_object.cpp
src/detail/meta_object.cpp
src/detail/monotonic_buffer_resource.cpp
src/detail/parse.cpp
src/detail/parse.cpp
src/detail/parser/chars.cpp
src/detail/parser/chars.cpp
src/detail/pretty_type_name.cpp
src/detail/pretty_type_name.cpp
...
@@ -250,6 +251,7 @@ caf_add_component(
...
@@ -250,6 +251,7 @@ caf_add_component(
detail.limited_vector
detail.limited_vector
detail.local_group_module
detail.local_group_module
detail.meta_object
detail.meta_object
detail.monotonic_buffer_resource
detail.parse
detail.parse
detail.parser.read_bool
detail.parser.read_bool
detail.parser.read_config
detail.parser.read_config
...
...
libcaf_core/caf/detail/monotonic_buffer_resource.hpp
0 → 100644
View file @
d1573452
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include <cstddef>
#include <map>
#include "caf/config.hpp"
#include "caf/detail/core_export.hpp"
#ifdef CAF_CLANG
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wc99-extensions"
#elif defined(CAF_GCC)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wpedantic"
#elif defined(CAF_MSVC)
# pragma warning(push)
# pragma warning(disable : 4200)
#endif
namespace
caf
::
detail
{
/// Replacement for `std::pmr::monotonic_buffer_resource`, which sadly is not
/// available on all platforms CAF currenlty supports. This resource does not
/// support upstream resources and instead always uses `malloc` and `free`.
class
CAF_CORE_EXPORT
monotonic_buffer_resource
{
public:
// -- member types -----------------------------------------------------------
/// A single block of memory.
struct
block
{
block
*
next
;
std
::
byte
bytes
[];
};
/// A bucket for storing multiple blocks.
struct
bucket
{
block
*
head
=
nullptr
;
std
::
byte
*
curr_pos
=
nullptr
;
std
::
byte
*
curr_end
=
nullptr
;
block
*
spare
=
nullptr
;
size_t
block_size
=
0
;
};
template
<
class
T
>
class
allocator
{
public:
using
value_type
=
T
;
template
<
class
U
>
struct
rebind
{
using
other
=
allocator
<
U
>
;
};
explicit
allocator
(
monotonic_buffer_resource
*
mbr
)
:
mbr_
(
mbr
)
{
// nop
}
allocator
()
:
mbr_
(
nullptr
)
{
// nop
}
allocator
(
const
allocator
&
)
=
default
;
allocator
&
operator
=
(
const
allocator
&
)
=
default
;
template
<
class
U
>
allocator
(
const
allocator
<
U
>&
other
)
:
mbr_
(
other
.
resource
())
{
// nop
}
template
<
class
U
>
allocator
&
operator
=
(
const
allocator
<
U
>&
other
)
{
mbr_
=
other
.
resource
();
return
*
this
;
}
T
*
allocate
(
size_t
n
)
{
return
static_cast
<
T
*>
(
mbr_
->
allocate
(
sizeof
(
T
)
*
n
,
alignof
(
T
)));
}
constexpr
void
deallocate
(
void
*
,
size_t
)
noexcept
{
// nop
}
constexpr
auto
resource
()
const
noexcept
{
return
mbr_
;
}
private:
monotonic_buffer_resource
*
mbr_
;
};
// -- constructors, destructors, and assignment operators --------------------
monotonic_buffer_resource
();
~
monotonic_buffer_resource
();
// -- allocator interface ----------------------------------------------------
/// Release all allocated memory to the OS even if no destructors were called
/// for the allocated objects.
void
release
();
/// Reclaims all allocated memory (re-using it) even if no destructors were
/// called for the allocated objects.
void
reclaim
();
/// Allocates memory.
[[
nodiscard
]]
void
*
allocate
(
size_t
bytes
,
size_t
alignment
=
alignof
(
max_align_t
));
/// Fancy no-op.
constexpr
void
deallocate
(
void
*
,
size_t
,
size_t
=
alignof
(
max_align_t
))
{
// nop
}
/// Counts how many blocks currently exist in the bucket for @p alloc_size.
size_t
blocks
(
size_t
alloc_size
);
/// Counts how many blocks currently exist in total.
size_t
blocks
();
private:
// Counts how many blocks exist in `where`.
size_t
blocks
(
bucket
&
where
);
// Gets the next free memory chunk.
[[
nodiscard
]]
void
*
do_alloc
(
bucket
&
from
,
size_t
bytes
,
size_t
alignment
);
// Adds another block to the bucket.
void
grow
(
bucket
&
what
);
// Select a bucket based on the allocation size.
bucket
&
bucket_by_size
(
size_t
alloc_size
);
// Sets all pointers back to nullptr.
void
reset
(
bucket
&
bkt
);
// Releases all memory in the bucket, leaving it in an invalid state.
void
release
(
bucket
&
bkt
);
// Shifts all blocks to `spare` list.
void
reclaim
(
bucket
&
bkt
);
// Objects of size <= 64 bytes.
bucket
small_
;
// Objects of size <= 512 bytes.
bucket
medium_
;
// Objects of various sizes > 512 bytes.
std
::
map
<
size_t
,
bucket
>
var_
;
};
}
// namespace caf::detail
#ifdef CAF_CLANG
# pragma clang diagnostic pop
#elif defined(CAF_GCC)
# pragma GCC diagnostic pop
#elif defined(CAF_MSVC)
# pragma warning(pop)
#endif
libcaf_core/src/detail/monotonic_buffer_resource.cpp
0 → 100644
View file @
d1573452
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/detail/monotonic_buffer_resource.hpp"
#include <limits>
#include <memory>
#include "caf/raise_error.hpp"
namespace
caf
::
detail
{
monotonic_buffer_resource
::
monotonic_buffer_resource
()
{
// 8kb blocks for the small and medium sized buckets.
small_
.
block_size
=
8
*
1024
;
medium_
.
block_size
=
8
*
1024
;
}
monotonic_buffer_resource
::~
monotonic_buffer_resource
()
{
release
(
small_
);
release
(
medium_
);
for
(
auto
&
kvp
:
var_
)
release
(
kvp
.
second
);
}
void
monotonic_buffer_resource
::
release
()
{
release
(
small_
);
reset
(
small_
);
release
(
medium_
);
reset
(
medium_
);
for
(
auto
&
kvp
:
var_
)
{
release
(
kvp
.
second
);
reset
(
kvp
.
second
);
}
}
void
monotonic_buffer_resource
::
reclaim
()
{
reclaim
(
small_
);
reclaim
(
medium_
);
for
(
auto
&
kvp
:
var_
)
reclaim
(
kvp
.
second
);
}
void
*
monotonic_buffer_resource
::
allocate
(
size_t
bytes
,
size_t
alignment
)
{
return
do_alloc
(
bucket_by_size
(
bytes
),
bytes
,
alignment
);
}
size_t
monotonic_buffer_resource
::
blocks
(
size_t
alloc_size
)
{
return
blocks
(
bucket_by_size
(
alloc_size
));
}
size_t
monotonic_buffer_resource
::
blocks
()
{
auto
result
=
blocks
(
small_
)
+
blocks
(
medium_
);
for
(
auto
&
kvp
:
var_
)
result
+=
blocks
(
kvp
.
second
);
return
result
;
}
size_t
monotonic_buffer_resource
::
blocks
(
bucket
&
where
)
{
size_t
result
=
0
;
for
(
auto
ptr
=
where
.
head
;
ptr
!=
nullptr
;
ptr
=
ptr
->
next
)
++
result
;
for
(
auto
ptr
=
where
.
spare
;
ptr
!=
nullptr
;
ptr
=
ptr
->
next
)
++
result
;
return
result
;
}
void
*
monotonic_buffer_resource
::
do_alloc
(
bucket
&
from
,
size_t
bytes
,
size_t
alignment
)
{
for
(;;)
{
if
(
from
.
curr_pos
!=
nullptr
)
{
auto
result
=
static_cast
<
void
*>
(
from
.
curr_pos
);
auto
space
=
static_cast
<
size_t
>
(
from
.
curr_end
-
from
.
curr_pos
);
if
(
std
::
align
(
alignment
,
bytes
,
result
,
space
))
{
from
.
curr_pos
=
static_cast
<
std
::
byte
*>
(
result
)
+
bytes
;
return
result
;
}
}
// Try again after allocating more storage.
grow
(
from
);
}
}
void
monotonic_buffer_resource
::
grow
(
bucket
&
what
)
{
auto
init
=
[
&
what
](
block
*
blk
)
{
blk
->
next
=
what
.
head
;
what
.
head
=
blk
;
what
.
curr_pos
=
blk
->
bytes
;
what
.
curr_end
=
reinterpret_cast
<
std
::
byte
*>
(
blk
)
+
what
.
block_size
;
};
if
(
what
.
spare
!=
nullptr
)
{
auto
blk
=
what
.
spare
;
what
.
spare
=
blk
->
next
;
init
(
blk
);
}
else
if
(
auto
ptr
=
malloc
(
what
.
block_size
))
{
init
(
static_cast
<
block
*>
(
ptr
));
}
else
{
CAF_RAISE_ERROR
(
std
::
bad_alloc
,
"monotonic_buffer_resource"
);
}
}
monotonic_buffer_resource
::
bucket
&
monotonic_buffer_resource
::
bucket_by_size
(
size_t
alloc_size
)
{
constexpr
auto
max_alloc_size
=
std
::
numeric_limits
<
size_t
>::
max
()
-
sizeof
(
block
)
-
alignof
(
max_align_t
);
auto
var_bucket
=
[
this
](
size_t
key
,
size_t
block_size
)
->
bucket
&
{
if
(
auto
i
=
var_
.
find
(
key
);
i
!=
var_
.
end
())
{
return
i
->
second
;
}
else
{
bucket
tmp
;
tmp
.
block_size
=
block_size
;
return
var_
.
emplace
(
key
,
tmp
).
first
->
second
;
}
};
if
(
alloc_size
<=
64
)
{
return
small_
;
}
else
if
(
alloc_size
<=
512
)
{
return
medium_
;
}
else
if
(
alloc_size
<=
1'048'576
)
{
// 1MB
// Align on 1kb and reserve memory for up to four elements.
auto
bucket_key
=
((
alloc_size
/
1024
)
+
1
)
*
1024
;
return
var_bucket
(
bucket_key
,
bucket_key
*
4
);
}
else
if
(
alloc_size
<=
max_alloc_size
)
{
// Fall back to individual allocations.
return
var_bucket
(
alloc_size
,
alloc_size
+
sizeof
(
block
)
+
alignof
(
max_align_t
));
}
else
{
CAF_RAISE_ERROR
(
std
::
bad_alloc
,
"monotonic_buffer_resource"
);
}
}
void
monotonic_buffer_resource
::
reset
(
bucket
&
bkt
)
{
bkt
.
head
=
nullptr
;
bkt
.
curr_pos
=
nullptr
;
bkt
.
curr_end
=
nullptr
;
bkt
.
spare
=
nullptr
;
}
void
monotonic_buffer_resource
::
release
(
bucket
&
bkt
)
{
for
(
auto
ptr
=
bkt
.
head
;
ptr
!=
nullptr
;)
{
auto
blk
=
ptr
;
ptr
=
ptr
->
next
;
free
(
blk
);
}
for
(
auto
ptr
=
bkt
.
spare
;
ptr
!=
nullptr
;)
{
auto
blk
=
ptr
;
ptr
=
ptr
->
next
;
free
(
blk
);
}
}
void
monotonic_buffer_resource
::
reclaim
(
bucket
&
bkt
)
{
for
(
auto
ptr
=
bkt
.
head
;
ptr
!=
nullptr
;)
{
auto
blk
=
ptr
;
ptr
=
ptr
->
next
;
blk
->
next
=
bkt
.
spare
;
bkt
.
spare
=
blk
;
}
bkt
.
head
=
nullptr
;
bkt
.
curr_pos
=
nullptr
;
bkt
.
curr_end
=
nullptr
;
}
}
// namespace caf::detail
libcaf_core/test/detail/monotonic_buffer_resource.cpp
0 → 100644
View file @
d1573452
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE detail.monotonic_buffer_resource
#include "caf/detail/monotonic_buffer_resource.hpp"
#include "core-test.hpp"
#include <list>
#include <map>
#include <vector>
using
namespace
caf
;
SCENARIO
(
"monotonic buffers group allocations"
)
{
GIVEN
(
"a monotonic buffer resource"
)
{
detail
::
monotonic_buffer_resource
mbr
;
WHEN
(
"calling allocate multiple times for the same size"
)
{
THEN
(
"the resource returns consecutive pointers"
)
{
CHECK_EQ
(
mbr
.
blocks
(
8
),
0u
);
auto
p1
=
mbr
.
allocate
(
8
);
auto
p2
=
mbr
.
allocate
(
8
);
auto
p3
=
mbr
.
allocate
(
8
);
CHECK_EQ
(
mbr
.
blocks
(
8
),
1u
);
CHECK
(
p1
<
p2
);
CHECK
(
p2
<
p3
);
}
}
}
GIVEN
(
"a monotonic buffer resource"
)
{
detail
::
monotonic_buffer_resource
mbr
;
WHEN
(
"calling allocate with various sizes"
)
{
THEN
(
"the resource puts allocations into buckets"
)
{
void
*
unused
=
nullptr
;
// For silencing nodiscard warnings.
CHECK_EQ
(
mbr
.
blocks
(),
0u
);
MESSAGE
(
"perform small allocations"
);
unused
=
mbr
.
allocate
(
64
);
CHECK_EQ
(
mbr
.
blocks
(),
1u
);
unused
=
mbr
.
allocate
(
64
);
CHECK_EQ
(
mbr
.
blocks
(),
1u
);
MESSAGE
(
"perform medium allocations"
);
unused
=
mbr
.
allocate
(
65
);
CHECK_EQ
(
mbr
.
blocks
(),
2u
);
unused
=
mbr
.
allocate
(
512
);
CHECK_EQ
(
mbr
.
blocks
(),
2u
);
MESSAGE
(
"perform large allocations <= 1 MB (pools allocations)"
);
unused
=
mbr
.
allocate
(
513
);
CHECK_EQ
(
mbr
.
blocks
(),
3u
);
unused
=
mbr
.
allocate
(
1023
);
CHECK_EQ
(
mbr
.
blocks
(),
3u
);
MESSAGE
(
"perform large allocations > 1 MB (allocates individually)"
);
unused
=
mbr
.
allocate
(
1'048'577
);
CHECK_EQ
(
mbr
.
blocks
(),
4u
);
unused
=
mbr
.
allocate
(
1'048'577
);
CHECK_EQ
(
mbr
.
blocks
(),
5u
);
}
}
}
}
SCENARIO
(
"monotonic buffers re-use memory after calling reclaim"
)
{
std
::
vector
<
void
*>
locations
;
GIVEN
(
"a monotonic buffer resource with some allocations performed on it"
)
{
detail
::
monotonic_buffer_resource
mbr
;
locations
.
push_back
(
mbr
.
allocate
(
64
));
locations
.
push_back
(
mbr
.
allocate
(
64
));
locations
.
push_back
(
mbr
.
allocate
(
65
));
locations
.
push_back
(
mbr
.
allocate
(
512
));
locations
.
push_back
(
mbr
.
allocate
(
513
));
locations
.
push_back
(
mbr
.
allocate
(
1023
));
locations
.
push_back
(
mbr
.
allocate
(
1'048'577
));
locations
.
push_back
(
mbr
.
allocate
(
1'048'577
));
WHEN
(
"calling reclaim on the resource"
)
{
mbr
.
reclaim
();
THEN
(
"performing the same allocations returns the same addresses again"
)
{
if
(
CHECK_EQ
(
mbr
.
blocks
(),
5u
))
{
CHECK_EQ
(
locations
[
0
],
mbr
.
allocate
(
64
));
CHECK_EQ
(
locations
[
1
],
mbr
.
allocate
(
64
));
CHECK_EQ
(
locations
[
2
],
mbr
.
allocate
(
65
));
CHECK_EQ
(
locations
[
3
],
mbr
.
allocate
(
512
));
CHECK_EQ
(
locations
[
4
],
mbr
.
allocate
(
513
));
CHECK_EQ
(
locations
[
5
],
mbr
.
allocate
(
1023
));
CHECK_EQ
(
locations
[
6
],
mbr
.
allocate
(
1'048'577
));
CHECK_EQ
(
locations
[
7
],
mbr
.
allocate
(
1'048'577
));
CHECK_EQ
(
mbr
.
blocks
(),
5u
);
}
}
}
}
}
SCENARIO
(
"monotonic buffers provide storage for STL containers"
)
{
GIVEN
(
"a monotonic buffer resource and a std::vector"
)
{
using
int_allocator
=
detail
::
monotonic_buffer_resource
::
allocator
<
int32_t
>
;
detail
::
monotonic_buffer_resource
mbr
;
WHEN
(
"pushing to the vector"
)
{
THEN
(
"the memory resource fills up"
)
{
CHECK_EQ
(
mbr
.
blocks
(),
0u
);
std
::
vector
<
int32_t
,
int_allocator
>
xs
{
int_allocator
{
&
mbr
}};
xs
.
push_back
(
42
);
CHECK_EQ
(
xs
.
size
(),
1u
);
CHECK_EQ
(
mbr
.
blocks
(),
1u
);
xs
.
insert
(
xs
.
end
(),
17
,
0
);
}
}
}
GIVEN
(
"a monotonic buffer resource and a std::list"
)
{
using
int_allocator
=
detail
::
monotonic_buffer_resource
::
allocator
<
int32_t
>
;
detail
::
monotonic_buffer_resource
mbr
;
WHEN
(
"pushing to the list"
)
{
THEN
(
"the memory resource fills up"
)
{
CHECK_EQ
(
mbr
.
blocks
(),
0u
);
std
::
list
<
int32_t
,
int_allocator
>
xs
{
int_allocator
{
&
mbr
}};
xs
.
push_back
(
42
);
CHECK_EQ
(
xs
.
size
(),
1u
);
CHECK_EQ
(
mbr
.
blocks
(),
1u
);
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment