Memory usage#
This section tries to give an overview why and how memory is being used in aes70 devices implemented using libaes70. It aims to help understand how much memory will be used by a device and what can be done to reduce memory usage when it becomes a problem.
In general memory is used in the following areas.
AES70 objects#
Each AES70 object will have a certain size. This can be broken down into the following items
Description | Size | Comments |
---|---|---|
vtable pointer | sizeof(void*) |
|
object number | 4 |
Can be reduced using AES70_SHORT_OBJECT_NUMBERS . |
device pointer | sizeof(void*) |
Can be removed using AES70_OPTIMIZE_SINGLE_DEVICE . |
each property change event | sizeof(void*) |
Can be avoided using static methods to emit events. |
implementation object | ? |
The implementation object will usually at least contain the role name (e.g. const char *) and either the property value itself (or an index - for instance - to identify the channel).
This means that without any additional tricks and taking advantage of the optimizations mentioned in the
table one has to assume that each object will require 4 * sizeof(void*)
bytes.
Without taking advantage of these optimizations and when using things like std::string
the size can become
much larger.
Devices#
Devices contain the object tree. This means that they usually store at least one pointer/reference for each
object. Static devices can in principle be built by storing objects directly in place,
which means that this can be further reduced. In static devices it is best to measure their size using sizeof
to understand in detail how much memory they use.
The situation in dynamic devices is more complex:
-
Dynamic devices using
aes70::dynamic_device::device
will allocate each object using ashared_ptr
which comes with a certain overhead depending on the implementation. Then, all objects will be store in astd::unordered_map
by their object number. In addition to that, eachaes70::dynamic_device::block
will containshared_ptr
to each object in astd::vector
. This means that the memory used will be at least2 * sizeof(shared_ptr) + 4
per object stored in the device. -
Dynamic devices using
aes70::dynamic_device::linear_device
will store objects in a globalstd::vector
instead of usingstd::shared_ptr
. This can reduce memory usage quite a bit, however at least2 * sizeof(shared_ptr)
per object.
In both situations it helps to take advantage of the reserve()
methods in the device and block classes to pre-allocate
sufficient capacity to store all object before adding them to the device. Still the above implementation does add
overhead which cannot be avoided.
Connections#
Connections contain network receive and send buffers. Their size changes dynamically and depends on the use cases. On top of that, connections also store event subscriptions made by controllers. The size needed for these subscriptions can be significant and is important to understand when trying to reduce memory usage in a device.
Static devies#
Static devices assign each event inside of a device an index. Most objects will either emit property change events or no events. There are in principle objects which can have more than one event, but those are rare. Subscriptions are then stored in each connection based on the index. The size of each subscription depends on global configuration:
Configuration | Size in bytes | Comments |
---|---|---|
default | 1/8 + 4 |
|
AES70_DISABLE_EV1 | 1/8 |
This will only work with controllers with EV2 support (aes70-2023). |
AES70_ALLOW_ONLY_SIMPLE_SUBSCRIPTIONS | 1/8 |
This might not work with some legacy controllers. |
Dynamic Devices#
In dynamic devices, property change subscriptions will be stored seperately from other event suscriptions if they are
simple subscriptions (made using the same OcaMethod
or all-zero OcaMethod
).
Since in most devices the vast majority of events are property change events they make up the vast majority of memory usage.
The size used by them depends on the configuration and dynamic device type used:
- In
aes70::dynamic_device::linear_device
simple property change subscriptions will use1/8
bytes each. - In
aes70::dynaic_device::device
simple property change subscriptions will use4 + 1/8
bytes each. In addition there is some amount of overhead for thestd::unordered_map
used.
If non-simple subscriptions are supported, i.e. when AES70_ALLOW_ONLY_SIMPLE_SUBSCRIPTIONS
and AES70_DISABLE_EV1
are
both off, each non-simple subscriptions or subscriptions which are not property change events require:
- Each EV1 subscription will use
12
bytes per event. - Each EV2 subscription will use
8
bytes per event.
In addition there will be some overhead for the std::unordered_set
and std::unordered_map
used.
Note
In summary, when memory usage neeeds to be reduced, it makes sense to enable AES70_ALLOW_ONLY_SIMPLE_SUBSCRIPTIONS
.
Alternatively, in situations in which the all supported controller applications are known and support EV2,
AES70_DISABLE_EV1
can be enabled.