Memory usage#
This section provides an overview of why and how memory is used in AES70 devices implemented using libaes70. It aims to help you 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 components:
| 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 contain at least 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 optimizations and taking advantage of the optimizations mentioned in the
table above, one should assume that each object will require 4 * sizeof(void*) bytes.
Without taking advantage of these optimizations and when using data structures like std::string, the size can become
significantly larger.
Devices#
Devices contain the object tree. This means that they usually store at least one pointer or reference for each
object. Static devices can in principle be built by storing objects directly in place,
which means that this overhead can be further reduced. For 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::devicewill allocate each object using ashared_ptr, which comes with a certain overhead depending on the implementation. Then, all objects will be stored in astd::unordered_mapby their object number. In addition to that, eachaes70::dynamic_device::blockwill contain ashared_ptrto each object in astd::vector. This means that the memory used will be at least2 * sizeof(shared_ptr) + 4bytes per object stored in the device. -
Dynamic devices using
aes70::dynamic_device::linear_devicewill store objects in a globalstd::vectorinstead of usingstd::shared_ptr. This can reduce memory usage quite a bit; however, it will still use 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 objects before adding them to the device. Still, the above implementation does add
overhead that 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 devices#
Static devices assign each event inside a device an index. Most objects will either emit property change events or no events. There are, in principle, objects that 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 the 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 separately from other event subscriptions if they are
simple subscriptions (made using the same OcaMethod or all-zero OcaMethod).
In most devices the vast majority of events are property change events, and these make up the vast majority of memory usage.
The size used by property change events depends on the configuration and dynamic device type used:
- In
aes70::dynamic_device::linear_device, simple property change subscriptions will use1/8bytes each. - In
aes70::dynamic_device::device, simple property change subscriptions will use4 + 1/8bytes each. In addition, there is some amount of overhead for thestd::unordered_mapused.
If non-simple subscriptions are supported (i.e., when AES70_ALLOW_ONLY_SIMPLE_SUBSCRIPTIONS and AES70_DISABLE_EV1 are
both disabled), each non-simple subscription or subscription that is not a property change event requires:
- Each EV1 subscription will use
12bytes per event. - Each EV2 subscription will use
8bytes 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 needs to be reduced, it makes sense to enable AES70_ALLOW_ONLY_SIMPLE_SUBSCRIPTIONS.
Alternatively, in situations where all supported controller applications are known and support EV2,
AES70_DISABLE_EV1 can be enabled.