mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-10-20 21:01:57 +00:00
117 lines
5.1 KiB
Markdown
117 lines
5.1 KiB
Markdown
% SPDX-License-Identifier: Apache-2.0
|
||||
|
|
(smalldevices-page)=
|
|||
|
|
|
|||
|
|
# Running EOS on Small Devices
|
|||
|
|
|
|||
|
|
EOS runs on single-board computers with 512 MB to 1 GB of RAM and on eMMC or SD storage.
|
|||
|
|
With the default configuration it needs about 290 MB at idle and 400–450 MB while a GENETIC
|
|||
|
|
run is in progress. The settings on this page bring that down to about 140 MB, without
|
|||
|
|
changing the optimization result.
|
|||
|
|
|
|||
|
|
All figures below were measured with GENETIC at 300 individuals × 400 generations and
|
|||
|
|
15-minute slots; the individual measurements state their conditions.
|
|||
|
|
|
|||
|
|
## Summary
|
|||
|
|
|
|||
|
|
| Setting | Effect | Plan |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `optimization.genetic.fitness_cache_max_entries: 0` [^pr1376] | −110 to −160 MB peak per run | identical |
|
|||
|
|
| `general.timezone_override` [^pr1377] | −28 MB at startup (no timezone lookup) | identical |
|
|||
|
|
| `optimization.self_consumption_interpolator: numpy` [^pr1378] | −37 MB after the first run (no SciPy) | identical |
|
|||
|
|
| `database.autosave_interval_sec: null` | storage writes from ~13 MB/min to ~1 KiB/min | identical, see notes |
|
|||
|
|
| `PYTHONMALLOC=malloc` (environment) | memory is returned after each run (glibc) | identical |
|
|||
|
|
| `OPENBLAS_NUM_THREADS=1` (environment) | no per-core BLAS buffers | identical |
|
|||
|
|
|
|||
|
|
[^pr1376]: Added by [#1376](https://github.com/Akkudoktor-EOS/EOS/pull/1376).
|
|||
|
|
[^pr1377]: Added by [#1377](https://github.com/Akkudoktor-EOS/EOS/pull/1377), which also imports
|
|||
|
|
optional heavy packages on first use (−123 MB at startup without any configuration change).
|
|||
|
|
[^pr1378]: Added by [#1378](https://github.com/Akkudoktor-EOS/EOS/pull/1378).
|
|||
|
|
|
|||
|
|
Example configuration:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"general": { "timezone_override": "Europe/Berlin" },
|
|||
|
|
"optimization": {
|
|||
|
|
"self_consumption_interpolator": "numpy",
|
|||
|
|
"genetic": { "fitness_cache_max_entries": 0 }
|
|||
|
|
},
|
|||
|
|
"database": { "autosave_interval_sec": null }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Environment of the EOS process (for example in the systemd unit):
|
|||
|
|
|
|||
|
|
```ini
|
|||
|
|
Environment=PYTHONMALLOC=malloc
|
|||
|
|
Environment=MALLOC_ARENA_MAX=2
|
|||
|
|
Environment=MALLOC_TRIM_THRESHOLD_=131072
|
|||
|
|
Environment=MALLOC_MMAP_THRESHOLD_=131072
|
|||
|
|
Environment=OPENBLAS_NUM_THREADS=1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Measurements
|
|||
|
|
|
|||
|
|
### Memory, all settings combined
|
|||
|
|
|
|||
|
|
Linux arm64 (Debian 13, glibc, Python 3.13), one CPU per container, server-like process
|
|||
|
|
(EOSdash off), 48 h horizon, 77 kWh battery with direct marketing; "with EV" adds an
|
|||
|
|
electric vehicle and a dynamic tariff.
|
|||
|
|
|
|||
|
|
| | Idle | Peak during a run | Peak with EV |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| `main` (default settings) | 282–288 MB | 401 MB | 451 MB |
|
|||
|
|
| all settings above | 136 MB | 142 MB | 144 MB |
|
|||
|
|
|
|||
|
|
With the fitness cache disabled, the memory no longer depends on the number of planned
|
|||
|
|
devices: with an EV and five home appliances the peak was 144 MB (about 300 MB with the
|
|||
|
|
unbounded cache). More devices only cost run time.
|
|||
|
|
|
|||
|
|
### Fitness cache
|
|||
|
|
|
|||
|
|
Same platform, two consecutive runs, `MALLOC_ARENA_MAX=2`, measured together with the lazy
|
|||
|
|
imports (server baseline 136.5 MB before the first run):
|
|||
|
|
|
|||
|
|
| Scenario | `fitness_cache_max_entries` | Peak RSS (anon) | Run time | Best fitness |
|
|||
|
|
|---|---|---|---|---|
|
|||
|
|
| base | `null` (unbounded) | 289.3 MB (231.3) | 89.5 s | −17.473689741050 |
|
|||
|
|
| base | `0` | 179.5 MB (121.1) | 88.7 s | −17.473689741050 |
|
|||
|
|
| with EV | `null` (unbounded) | 340.0 MB (282.0) | 131.6 s | −13.851295466546 |
|
|||
|
|
| with EV | `0` | 181.3 MB (123.3) | 133.2 s | −13.851295466546 |
|
|||
|
|
|
|||
|
|
Only 6–15 % of the cache lookups hit. A Raspberry Pi 4 with real installation data (see below)
|
|||
|
|
showed the same picture: the unbounded cache made a run about 3.5 % faster and cost 100 MB.
|
|||
|
|
|
|||
|
|
### Run time on real hardware
|
|||
|
|
|
|||
|
|
Data of a real installation (77 kWh battery, direct marketing, 24 h control horizon at
|
|||
|
|
15 minutes), all settings above:
|
|||
|
|
|
|||
|
|
| Device | Run time | Peak RSS |
|
|||
|
|
|---|---|---|
|
|||
|
|
| Raspberry Pi 4 Model B (2 GB, Cortex-A72), fitness cache off | 623–636 s | 195 MB |
|
|||
|
|
| Raspberry Pi 4 Model B, fitness cache unbounded | 602–609 s | 294 MB |
|
|||
|
|
| x86 VM, 2 vCPU (container, with EV) | 173 s | 127 MB |
|
|||
|
|
|
|||
|
|
On a Raspberry Pi 4 a run therefore takes about 10 minutes. With a 15-minute `ems.interval`
|
|||
|
|
the device is busy about 70 % of the time; a longer interval leaves more headroom.
|
|||
|
|
|
|||
|
|
### Storage writes
|
|||
|
|
|
|||
|
|
With the default `database.autosave_interval_sec` the measurement and prediction databases are
|
|||
|
|
written every few seconds. On an installation that pushes its forecasts and measurements from
|
|||
|
|
an external system this amounted to 12–14 MB per minute; with autosave disabled about
|
|||
|
|
1 KiB per minute remained. On SD cards and small eMMC this is the main source of wear.
|
|||
|
|
|
|||
|
|
Disabling autosave means that data pushed to EOS is lost when EOS restarts. That is fine when an
|
|||
|
|
external system (an energy manager, Home Assistant, Node-RED) pushes forecasts and measurements
|
|||
|
|
again; it is not suitable when EOS fetches and keeps its own history.
|
|||
|
|
|
|||
|
|
### Allocator
|
|||
|
|
|
|||
|
|
On glibc, Python keeps memory freed after a run in its own pools. Measured on an x86 VM with an
|
|||
|
|
unbounded fitness cache, `PYTHONMALLOC=malloc` together with the trim thresholds above lowered
|
|||
|
|
the idle memory from about 295 MB to 238 MB, and after each run EOS fell back to about 260 MB
|
|||
|
|
instead of staying at its peak. With the fitness cache disabled the difference is small
|
|||
|
|
(about 5 MB), because the large per-run allocations no longer happen.
|