esphome / esphome/feature-requests
Set `build_cache_dir` in platformio to reuse compiled objects between updates (and devices)
- Dominant language
- No language data
- Stars
- 450
- Forks
- 29
- PR merge metrics
- No merged PRs in 30d
Description
TL;DR: This issue is a journey I took to find out how to improve caching. Skip to [this comment](https://github.com/esphome/feature-requests/issues/2171#issuecomment-1473686421) where I used `build_cache_dir` in platformio to improve build speeds.
**Describe the problem you have/What new integration you would like**
I've been managing a few identical devices (apart from their identity) for a little while now, and I've been following along when it's been compiling and I see the same code being compiled again and again. This makes sense of course, because it's simpler to see each device as a completely separate unit.
I imagine that in my and many other instances though, at least parts of these files will be identical. It is my impression that the device config is used to substitute values into the source (`cpp`) files, which is then compiled. This could be f.ex. the wifi configuration, which would (at minimum) contain an SSID to connect to. This SSID is the same though, on all my devices. So I'm thinking it should be possible to reuse the compiled output (the `.o` file). Note: I've since found that the SSID/Wifi config is actually in `main.cpp`, which just makes the savings greater for the wifi library, as it will be identical "always".
If I'm right in assuming the `.cpp` files are modified with the values they need (from the device config), it should be possible to do something like:
1. Substitute in the values needed in the `.cpp` file
2. Hash the `.cpp` file, f.ex. using `md5` or something less intensive like [`xxhash`](https://github.com/Cyan4973/xxHash)
3. Check if a file exists at `/somedir/output/HASH.o`
4. If the file exists, copy it in to the output, where the output would normally go. We know this output is identical, to what we would have produced, as the source input is identical
5. If the file does not exist, compile the `.cpp`, and copy the output to `/somedir/output/HASH.o`
6. Proceed as normal, perfom linking as normal, build a firmware as normal - this should work, as all compiled output will still be in the place it usually is.
The temporary/cache location can be cleared at will - if it's empty, the sources are compiled again and placed there. Likewise, if there are access timestamps, you can remove the least recently used files in order to keep a rolling set of used files.
There are some drawbacks that I can think of:
* Caching the output based on the source input will work for all instances where the output is deterministic. If the output isn't, it doesn't "work" - this could be if the compiler changes.
* If the compiler changes, it's likely we get a new `esphome` addon served. So one way to handle this is to let the cache directory be an ephemeral location, like a path that isn't mapped to a persistent volume. This way the cache is cleared on each esphome addon update.
**Please describe your use case for this integration and alternatives you've tried:**
N/A
**Additional context**
I browsed to my `/data` directory on my esphome addon and checked some of the compiled files. I just grabbed two from the list and found this. I'm in the middle of an update across all devices.
```
# find -name 'api_connection.cpp.o' -exec md5sum {} \; | sort
0a6a67845634bf12c0e6c1aea62d94d4 ./light-44-2/.pioenvs/light-44-2/src/esphome/components/api/api_connection.cpp.o
0a6a67845634bf12c0e6c1aea62d94d4 ./light-extra01/.pioenvs/light-extra01/src/esphome/components/api/api_connection.cpp.o
3e450948cd55c7283372197e388d7da5 ./athom-rgbct-light-dfb640/.pioenvs/athom-rgbct-light-dfb640/src/esphome/components/api/api_connection.cpp.o
3e450948cd55c7283372197e388d7da5 ./athom-rgbct-light-dfb727/.pioenvs/athom-rgbct-light-dfb727/src/esphome/components/api/api_connection.cpp.o
66eeb0ca149319f7690ea22dd02efe73 ./athom-relay-board-x2-4dab15/.pioenvs/athom-relay-board-x2-4dab15/src/esphome/components/api/api_connection.cpp.o
66eeb0ca149319f7690ea22dd02efe73 ./relay-40-4/.pioenvs/relay-40-4/src/esphome/components/api/api_connection.cpp.o
a01245756f2737b7213a88c97faccfe0 ./light-1-5/.pioenvs/light-1-5/src/esphome/components/api/api_connection.cpp.o
a01245756f2737b7213a88c97faccfe0 ./light-41-1/.pioenvs/light-41-1/src/esphome/components/api/api_connection.cpp.o
a01245756f2737b7213a88c97faccfe0 ./light-41-2/.pioenvs/light-41-2/src/esphome/components/api/api_connection.cpp.o
a01245756f2737b7213a88c97faccfe0 ./light-41-3/.pioenvs/light-41-3/src/esphome/components/api/api_connection.cpp.o
a01245756f2737b7213a88c97faccfe0 ./light-41-4/.pioenvs/light-41-4/src/esphome/components/api/api_connection.cpp.o
a01245756f2737b7213a88c97faccfe0 ./light-41-5/.pioenvs/light-41-5/src/esphome/components/api/api_connection.cpp.o
a01245756f2737b7213a88c97faccfe0 ./light-41-6/.pioenvs/light-41-6/src/esphome/components/api/api_connection.cpp.o
a01245756f2737b7213a88c97faccfe0 ./light-44-1/.pioenvs/light-44-1/src/esphome/components/api/api_connection.cpp.o
b46b335dc1eeaed4f0f5b61134da47a5 ./athom-front-1/.pioenvs/athom-front-1/src/esphome/components/api/api_connection.cpp.o
b46b335dc1eeaed4f0f5b61134da47a5 ./athom-front-3/.pioenvs/athom-front-3/src/esphome/components/api/api_connection.cpp.o
d9b8168af57fe60127898024c3d16ebd ./shelly-1e5c3c-7228/.pioenvs/shelly-1e5c3c-7228/src/esphome/components/api/api_connection.cpp.o
e8ca504e2e9920ddc904110c637ea0c6 ./light-43-1/.pioenvs/light-43-1/src/esphome/components/api/api_connection.cpp.o
e8ca504e2e9920ddc904110c637ea0c6 ./light-43-2/.pioenvs/light-43-2/src/esphome/components/api/api_connection.cpp.o
e8ca504e2e9920ddc904110c637ea0c6 ./light-43-3/.pioenvs/light-43-3/src/esphome/components/api/api_connection.cpp.o
root@5c53de3b-esphome:/data# find -name 'proto.cpp.o' -exec md5sum {} \; | sort
aef2b21dd7852f77ffe9725946b561e2 ./athom-front-1/.pioenvs/athom-front-1/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./athom-front-3/.pioenvs/athom-front-3/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./athom-relay-board-x2-4dab15/.pioenvs/athom-relay-board-x2-4dab15/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./athom-rgbct-light-dfb640/.pioenvs/athom-rgbct-light-dfb640/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./athom-rgbct-light-dfb727/.pioenvs/athom-rgbct-light-dfb727/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-1-5/.pioenvs/light-1-5/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-41-1/.pioenvs/light-41-1/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-41-2/.pioenvs/light-41-2/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-41-3/.pioenvs/light-41-3/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-41-4/.pioenvs/light-41-4/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-41-5/.pioenvs/light-41-5/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-41-6/.pioenvs/light-41-6/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-43-1/.pioenvs/light-43-1/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-43-2/.pioenvs/light-43-2/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-43-3/.pioenvs/light-43-3/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-44-1/.pioenvs/light-44-1/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-44-2/.pioenvs/light-44-2/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./light-extra01/.pioenvs/light-extra01/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./relay-40-4/.pioenvs/relay-40-4/src/esphome/components/api/proto.cpp.o
aef2b21dd7852f77ffe9725946b561e2 ./shelly-1e5c3c-7228/.pioenvs/shelly-1e5c3c-7228/src/esphome/components/api/proto.cpp.o
```
So here, we see the hashes of the compiled output of two files: `api_connection.cpp` and `proto.cpp`. In the first case, there are 5 distinct versions right now across 14 devices (plus a few I have renamed). For Proto.cpp, there is just the one output.
In this case, if the output was cached, I could potentially save 13 compilations of the `proto.cpp` file.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reviewing the PlatformIO build_cache_dir setting and the compiled outputs under .pioenvs/*/src/esphome/components/api/, especially api_connection.cpp.o and proto.cpp.o. Compare the repeated hashes described in the issue, then determine how cache reuse can preserve normal output and linking; done means identical sources reuse compiled objects without changing the resulting firmware.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- build-system, embedded-iot, performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100