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
66dba7e3
Commit
66dba7e3
authored
Nov 30, 2019
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Simplify and tune size-based credit controller
parent
fd14095d
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
50 additions
and
89 deletions
+50
-89
libcaf_core/caf/detail/size_based_credit_controller.hpp
libcaf_core/caf/detail/size_based_credit_controller.hpp
+17
-27
libcaf_core/caf/inbound_path.hpp
libcaf_core/caf/inbound_path.hpp
+0
-3
libcaf_core/src/complexity_based_credit_controller.cpp
libcaf_core/src/complexity_based_credit_controller.cpp
+2
-4
libcaf_core/src/defaults.cpp
libcaf_core/src/defaults.cpp
+2
-2
libcaf_core/src/inbound_path.cpp
libcaf_core/src/inbound_path.cpp
+8
-8
libcaf_core/src/size_based_credit_controller.cpp
libcaf_core/src/size_based_credit_controller.cpp
+21
-45
No files found.
libcaf_core/caf/detail/size_based_credit_controller.hpp
View file @
66dba7e3
...
@@ -22,10 +22,10 @@
...
@@ -22,10 +22,10 @@
#include <chrono>
#include <chrono>
namespace
caf
{
namespace
caf
::
detail
{
namespace
detail
{
/// .
/// A credit controller that estimates the bytes required to store incoming
/// batches and constrains credit based on upper bounds for memory usage.
class
size_based_credit_controller
:
public
credit_controller
{
class
size_based_credit_controller
:
public
credit_controller
{
public:
public:
// -- member types -----------------------------------------------------------
// -- member types -----------------------------------------------------------
...
@@ -60,26 +60,13 @@ private:
...
@@ -60,26 +60,13 @@ private:
// -- member variables -------------------------------------------------------
// -- member variables -------------------------------------------------------
/// Total number of elements in all processed batches in the current cycle.
/// Total number of elements in all processed batches in the current cycle.
int64_t
num_elements_
=
0
;
int64_t
num_batches_
=
0
;
/// Measured size of all sampled elements.
int64_t
total_size_
=
0
;
/// Elapsed time for processing all elements of all batches in the current
/// cycle.
timespan
processing_time_
;
/// Timestamp of the last call to `before_processing`.
time_point
processing_begin_
;
/// Stores the desired per-batch complexity.
timespan
complexity_
;
/// Stores how many elements the buffer should hold at most.
/// Stores how many elements the buffer should hold at most.
int32_t
buffer_size_
=
1
;
int32_t
buffer_size_
=
1
0
;
/// Stores how many elements each batch should contain.
/// Stores how many elements each batch should contain.
int32_t
batch_size_
=
1
;
int32_t
batch_size_
=
2
;
/// Configures how many bytes we store in total.
/// Configures how many bytes we store in total.
int32_t
buffer_capacity_
;
int32_t
buffer_capacity_
;
...
@@ -87,15 +74,18 @@ private:
...
@@ -87,15 +74,18 @@ private:
/// Configures how many bytes we transfer per batch.
/// Configures how many bytes we transfer per batch.
int32_t
bytes_per_batch_
;
int32_t
bytes_per_batch_
;
/// Stores the current write position in the ring buffer.
/// Stores how many elements we have sampled during the current cycle.
size_t
ringbuf_pos_
=
0
;
int64_t
sampled_elements_
=
0
;
/// Stores approximately how many bytes the sampled elements require when
/// serialized.
int64_t
sampled_total_size_
=
0
;
///
Stores how many valid entries the ring buffer holds
.
///
Counter for keeping track of when to sample a batch
.
size_t
ringbuf_size
_
=
0
;
int32_t
sample_counter
_
=
0
;
///
Records recent calculations for buffer and batch sizes
.
///
Configured how many batches we skip for the size sampling
.
std
::
array
<
std
::
pair
<
int32_t
,
int32_t
>
,
32
>
ringbuf_
;
int32_t
sample_rate_
=
50
;
};
};
}
// namespace detail
}
// namespace caf::detail
}
// namespace caf
libcaf_core/caf/inbound_path.hpp
View file @
66dba7e3
...
@@ -60,9 +60,6 @@ public:
...
@@ -60,9 +60,6 @@ public:
/// Amount of credit we have signaled upstream.
/// Amount of credit we have signaled upstream.
int32_t
assigned_credit
=
0
;
int32_t
assigned_credit
=
0
;
/// Upstream capacity for limiting the amount of credit we can give.
int32_t
downstream_capacity
=
0
;
/// Priority of incoming batches from this source.
/// Priority of incoming batches from this source.
stream_priority
prio
=
stream_priority
::
normal
;
stream_priority
prio
=
stream_priority
::
normal
;
...
...
libcaf_core/src/complexity_based_credit_controller.cpp
View file @
66dba7e3
...
@@ -25,8 +25,7 @@
...
@@ -25,8 +25,7 @@
// Safe us some typing and very ugly formatting.
// Safe us some typing and very ugly formatting.
#define impl complexity_based_credit_controller
#define impl complexity_based_credit_controller
namespace
caf
{
namespace
caf
::
detail
{
namespace
detail
{
impl
::
impl
(
scheduled_actor
*
self
)
:
super
(
self
)
{
impl
::
impl
(
scheduled_actor
*
self
)
:
super
(
self
)
{
auto
&
cfg
=
self
->
system
().
config
();
auto
&
cfg
=
self
->
system
().
config
();
...
@@ -80,5 +79,4 @@ credit_controller::assignment impl::compute(timespan cycle) {
...
@@ -80,5 +79,4 @@ credit_controller::assignment impl::compute(timespan cycle) {
return
result
;
return
result
;
}
}
}
// namespace detail
}
// namespace caf::detail
}
// namespace caf
libcaf_core/src/defaults.cpp
View file @
66dba7e3
...
@@ -53,8 +53,8 @@ const atom_value credit_policy = atom("complexity");
...
@@ -53,8 +53,8 @@ const atom_value credit_policy = atom("complexity");
namespace
size_policy
{
namespace
size_policy
{
const
int32_t
bytes_per_batch
=
4
*
1024
;
// 4 k
B
const
int32_t
bytes_per_batch
=
1024
;
// 1 K
B
const
int32_t
buffer_capacity
=
64
*
1024
;
// 64
k
B
const
int32_t
buffer_capacity
=
64
*
1024
;
// 64
K
B
}
// namespace size_policy
}
// namespace size_policy
...
...
libcaf_core/src/inbound_path.cpp
View file @
66dba7e3
...
@@ -38,7 +38,9 @@ constexpr bool force_ack = true;
...
@@ -38,7 +38,9 @@ constexpr bool force_ack = true;
void
emit_ack_batch
(
inbound_path
&
path
,
credit_controller
::
assignment
x
,
void
emit_ack_batch
(
inbound_path
&
path
,
credit_controller
::
assignment
x
,
bool
force_ack_msg
=
false
)
{
bool
force_ack_msg
=
false
)
{
CAF_ASSERT
(
x
.
batch_size
>
0
);
CAF_ASSERT
(
x
.
batch_size
>
0
);
auto
&
out
=
path
.
mgr
->
out
();
path
.
desired_batch_size
=
x
.
batch_size
;
path
.
desired_batch_size
=
x
.
batch_size
;
auto
downstream_capacity
=
out
.
max_capacity
();
auto
guard
=
detail
::
make_scope_guard
([
&
]
{
auto
guard
=
detail
::
make_scope_guard
([
&
]
{
if
(
!
force_ack_msg
||
path
.
up_to_date
())
if
(
!
force_ack_msg
||
path
.
up_to_date
())
return
;
return
;
...
@@ -47,14 +49,14 @@ void emit_ack_batch(inbound_path& path, credit_controller::assignment x,
...
@@ -47,14 +49,14 @@ void emit_ack_batch(inbound_path& path, credit_controller::assignment x,
path
.
self
()
->
address
(),
0
,
path
.
self
()
->
address
(),
0
,
x
.
batch_size
,
x
.
batch_size
,
path
.
last_batch_id
,
path
.
last_batch_id
,
path
.
downstream_capacity
));
downstream_capacity
));
path
.
last_acked_batch_id
=
path
.
last_batch_id
;
path
.
last_acked_batch_id
=
path
.
last_batch_id
;
});
});
auto
credit
=
std
::
min
(
x
.
credit
,
path
.
downstream_capacity
);
auto
max_credit
=
std
::
min
(
x
.
credit
,
downstream_capacity
);
if
(
credit
<=
path
.
assigned_credit
)
auto
used_credit
=
static_cast
<
int32_t
>
(
out
.
buffered
())
+
path
.
assigned_credit
;
if
(
max_credit
<=
used_credit
)
return
;
return
;
auto
new_credit
=
path
.
mgr
->
acquire_credit
(
&
path
,
auto
new_credit
=
path
.
mgr
->
acquire_credit
(
&
path
,
max_credit
-
used_credit
);
credit
-
path
.
assigned_credit
);
if
(
new_credit
<
1
)
if
(
new_credit
<
1
)
return
;
return
;
guard
.
disable
();
guard
.
disable
();
...
@@ -63,7 +65,7 @@ void emit_ack_batch(inbound_path& path, credit_controller::assignment x,
...
@@ -63,7 +65,7 @@ void emit_ack_batch(inbound_path& path, credit_controller::assignment x,
path
.
self
()
->
address
(),
path
.
self
()
->
address
(),
new_credit
,
x
.
batch_size
,
new_credit
,
x
.
batch_size
,
path
.
last_batch_id
,
path
.
last_batch_id
,
path
.
downstream_capacity
));
downstream_capacity
));
path
.
last_acked_batch_id
=
path
.
last_batch_id
;
path
.
last_acked_batch_id
=
path
.
last_batch_id
;
path
.
assigned_credit
+=
new_credit
;
path
.
assigned_credit
+=
new_credit
;
}
}
...
@@ -128,7 +130,6 @@ void inbound_path::emit_ack_open(local_actor* self, actor_addr rebind_from) {
...
@@ -128,7 +130,6 @@ void inbound_path::emit_ack_open(local_actor* self, actor_addr rebind_from) {
// Update state.
// Update state.
auto
initial
=
controller_
->
compute_initial
();
auto
initial
=
controller_
->
compute_initial
();
assigned_credit
=
mgr
->
acquire_credit
(
this
,
initial
.
credit
);
assigned_credit
=
mgr
->
acquire_credit
(
this
,
initial
.
credit
);
downstream_capacity
=
assigned_credit
;
CAF_ASSERT
(
assigned_credit
>=
0
);
CAF_ASSERT
(
assigned_credit
>=
0
);
desired_batch_size
=
std
::
min
(
initial
.
batch_size
,
assigned_credit
);
desired_batch_size
=
std
::
min
(
initial
.
batch_size
,
assigned_credit
);
// Make sure we receive errors from this point on.
// Make sure we receive errors from this point on.
...
@@ -146,7 +147,6 @@ void inbound_path::emit_ack_open(local_actor* self, actor_addr rebind_from) {
...
@@ -146,7 +147,6 @@ void inbound_path::emit_ack_open(local_actor* self, actor_addr rebind_from) {
void
inbound_path
::
emit_ack_batch
(
local_actor
*
,
int32_t
,
void
inbound_path
::
emit_ack_batch
(
local_actor
*
,
int32_t
,
actor_clock
::
time_point
now
,
timespan
cycle
)
{
actor_clock
::
time_point
now
,
timespan
cycle
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
slots
)
<<
CAF_ARG
(
cycle
));
CAF_LOG_TRACE
(
CAF_ARG
(
slots
)
<<
CAF_ARG
(
cycle
));
downstream_capacity
=
mgr
->
out
().
max_capacity
();
last_credit_decision
=
now
;
last_credit_decision
=
now
;
next_credit_decision
=
now
+
cycle
;
next_credit_decision
=
now
+
cycle
;
caf
::
emit_ack_batch
(
*
this
,
controller_
->
compute
(
cycle
),
force_ack
);
caf
::
emit_ack_batch
(
*
this
,
controller_
->
compute
(
cycle
),
force_ack
);
...
...
libcaf_core/src/size_based_credit_controller.cpp
View file @
66dba7e3
...
@@ -27,12 +27,10 @@
...
@@ -27,12 +27,10 @@
// Safe us some typing and very ugly formatting.
// Safe us some typing and very ugly formatting.
#define impl size_based_credit_controller
#define impl size_based_credit_controller
namespace
caf
{
namespace
caf
::
detail
{
namespace
detail
{
impl
::
impl
(
scheduled_actor
*
self
)
:
super
(
self
)
{
impl
::
impl
(
scheduled_actor
*
self
)
:
super
(
self
)
{
auto
&
cfg
=
self
->
system
().
config
();
auto
&
cfg
=
self
->
system
().
config
();
complexity_
=
cfg
.
stream_desired_batch_complexity
;
buffer_capacity_
=
get_or
(
cfg
,
"stream.size-policy.buffer-capacity"
,
buffer_capacity_
=
get_or
(
cfg
,
"stream.size-policy.buffer-capacity"
,
defaults
::
stream
::
size_policy
::
buffer_capacity
);
defaults
::
stream
::
size_policy
::
buffer_capacity
);
bytes_per_batch_
=
get_or
(
cfg
,
"stream.size-policy.bytes-per-batch"
,
bytes_per_batch_
=
get_or
(
cfg
,
"stream.size-policy.bytes-per-batch"
,
...
@@ -44,13 +42,16 @@ impl::~impl() {
...
@@ -44,13 +42,16 @@ impl::~impl() {
}
}
void
impl
::
before_processing
(
downstream_msg
::
batch
&
x
)
{
void
impl
::
before_processing
(
downstream_msg
::
batch
&
x
)
{
num_elements_
+=
x
.
xs_size
;
if
(
++
sample_counter_
==
sample_rate_
)
{
total_size_
=
serialized_size
(
self
()
->
system
(),
x
.
xs
);
sampled_elements_
+=
x
.
xs_size
;
processing_begin_
=
clock_type
::
now
();
sampled_total_size_
+=
serialized_size
(
self
()
->
system
(),
x
.
xs
);
sample_counter_
=
0
;
}
++
num_batches_
;
}
}
void
impl
::
after_processing
(
downstream_msg
::
batch
&
)
{
void
impl
::
after_processing
(
downstream_msg
::
batch
&
)
{
processing_time_
+=
clock_type
::
now
()
-
processing_begin_
;
// nop
}
}
credit_controller
::
assignment
impl
::
compute_initial
()
{
credit_controller
::
assignment
impl
::
compute_initial
()
{
...
@@ -58,9 +59,7 @@ credit_controller::assignment impl::compute_initial() {
...
@@ -58,9 +59,7 @@ credit_controller::assignment impl::compute_initial() {
}
}
credit_controller
::
assignment
impl
::
compute
(
timespan
)
{
credit_controller
::
assignment
impl
::
compute
(
timespan
)
{
// Update batch and buffer size if we have at least 10 new data points to
if
(
sampled_elements_
>
0
)
{
// work with.
if
(
num_elements_
>
9
)
{
// Helper for truncating a 64-bit integer to a 32-bit integer with a
// Helper for truncating a 64-bit integer to a 32-bit integer with a
// minimum value of 1.
// minimum value of 1.
auto
clamp_i32
=
[](
int64_t
x
)
->
int32_t
{
auto
clamp_i32
=
[](
int64_t
x
)
->
int32_t
{
...
@@ -71,40 +70,18 @@ credit_controller::assignment impl::compute(timespan) {
...
@@ -71,40 +70,18 @@ credit_controller::assignment impl::compute(timespan) {
return
1
;
return
1
;
return
static_cast
<
int32_t
>
(
x
);
return
static_cast
<
int32_t
>
(
x
);
};
};
// Calculate ideal batch size by complexity.
auto
total_ns
=
processing_time_
.
count
();
CAF_ASSERT
(
total_ns
>
0
);
auto
size_by_complexity
=
clamp_i32
((
complexity_
.
count
()
*
num_elements_
)
/
total_ns
);
// Calculate ideal batch size by size.
// Calculate ideal batch size by size.
auto
bytes_per_element
=
clamp_i32
(
total_size_
/
num_elements_
);
auto
bytes_per_element
=
clamp_i32
(
sampled_total_size_
/
sampled_elements_
);
auto
size_by_bytes
=
clamp_i32
(
bytes_per_batch_
/
bytes_per_element
);
batch_size_
=
clamp_i32
(
bytes_per_batch_
/
bytes_per_element
);
// Always pick the smaller of the two in order to achieve a good tradeoff
buffer_size_
=
clamp_i32
(
buffer_capacity_
/
bytes_per_element
);
// between size and computational complexity
auto
batch_size
=
clamp_i32
(
std
::
min
(
size_by_bytes
,
size_by_complexity
));
auto
buffer_size
=
std
::
min
(
clamp_i32
(
buffer_capacity_
/
bytes_per_element
),
4
*
batch_size_
);
// Add a new entry to our ringbuffer and calculate batch and buffer sizes
// based on the average recorded sizes.
auto
&
kvp
=
ringbuf_
[
ringbuf_pos_
];
kvp
.
first
=
buffer_size
;
kvp
.
second
=
batch_size
;
ringbuf_pos_
=
(
ringbuf_pos_
+
1
)
%
ringbuf_
.
size
();
if
(
ringbuf_size_
<
ringbuf_
.
size
())
++
ringbuf_size_
;
using
int32_pair
=
std
::
pair
<
int32_t
,
int32_t
>
;
auto
plus
=
[](
int32_pair
x
,
int32_pair
y
)
{
return
int32_pair
{
x
.
first
+
y
.
first
,
x
.
second
+
y
.
second
};
};
auto
psum
=
std
::
accumulate
(
ringbuf_
.
begin
(),
ringbuf_
.
begin
()
+
ringbuf_size_
,
int32_pair
{
0
,
0
},
plus
);
buffer_size_
=
psum
.
first
/
ringbuf_size_
;
batch_size_
=
psum
.
second
/
ringbuf_size_
;
// Reset bookkeeping state.
// Reset bookkeeping state.
num_elements_
=
0
;
sampled_elements_
=
0
;
total_size_
=
0
;
sampled_total_size_
=
0
;
processing_time_
=
timespan
{
0
};
// Ideally, we sample 10 batches per cycle.
sample_rate_
=
clamp_i32
(
num_batches_
/
10
);
if
(
sample_counter_
>=
sample_rate_
)
sample_counter_
=
0
;
num_batches_
=
0
;
}
}
return
{
buffer_size_
,
batch_size_
};
return
{
buffer_size_
,
batch_size_
};
}
}
...
@@ -116,8 +93,7 @@ credit_controller::assignment impl::compute_bridge() {
...
@@ -116,8 +93,7 @@ credit_controller::assignment impl::compute_bridge() {
}
}
int32_t
impl
::
low_threshold
()
const
noexcept
{
int32_t
impl
::
low_threshold
()
const
noexcept
{
return
buffer_size_
/
4
;
return
static_cast
<
int32_t
>
(
buffer_size_
*
.75
f
)
;
}
}
}
// namespace detail
}
// namespace caf::detail
}
// namespace caf
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