perf: smaller relay code, relay pin writes no longer clobber the load pins - #175
Merged
Merged
Conversation
… pins With two relays, basic is 396 bytes smaller (11566 -> 11170): - proceed_relays() reads the TEMA average once, not before every relay; proceed_relay() had two identical branches for the import threshold - try_turnON() and try_turnOFF() are one try_switch(bool) - setPinState() works out the port and the mask once - EWMA_average::addValue() and getAverageT() are no longer inlined setPinState() used a read-modify-write of PORTx from loop(). Had the ADC ISR changed a load pin of the same port in between, the old load state would have been written back, until the next decision. It now toggles the relay's bit through PINx: a single write, which leaves the other bits alone, with no need to disable interrupts. Only possible with a relay on the loads' port (D2-D7), so not with the default pins. Builds without relays are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setPinsON() and setPinsOFF() read-modify-write the ports: only safe from the ADC ISR, which cannot be interrupted. setPinON() and setPinOFF() are atomic only for a pin known at compile time. From loop(), setPinState() and togglePin() write through PINx. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inlined, a static instance's state is reached with absolute lds/sts, 4 bytes per byte accessed; out of line, through the this pointer with ld/st/ldd/std, 2 bytes. Not register spills, as the comment said. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two callers in a basic build, and each inlined copy would read the static filter's averages with absolute lds (4 bytes per byte), against ld/ldd through this (2 bytes): 70 bytes less out of line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The relay code is 396 bytes smaller, and a relay pin write can no longer clobber the load pins written by the ADC ISR.
With two relays (
RELAY_DIVERSION{ true }, relays on D8 and D9),basic: 11566 → 11170 bytes flash, RAM unchanged. Builds without relays are unchanged.proceed_relays()reads the TEMA average once, not before every relay; the two identical import-threshold branches ofproceed_relay()are onetry_turnON()andtry_turnOFF()are onetry_switch(bool)setPinState()works out the port and the mask once, instead of two inlined 3-way branches with variable shiftsEWMA_average::addValue()andgetAverageT()not inlined:getAverageT()was copied to every call site.addValue()has a single caller, but inlined it reaches the static filter's six int32 with absolutelds/sts(4 bytes per byte accessed); out of line it goes throughthiswithld/st/ldd/std(2 bytes)Relay pin writes
setPinState()did a read-modify-write ofPORTxfromloop()(in/or/out- a run-time mask can't usesbi/cbi). Had the ADC ISR changed a load pin of the same port in between,loop()would have written the old load state back, until the next decision rewrote it (up to one mains cycle).The relay's bit is now toggled through
PINx: writing a 1 there toggles that bit ofPORTx, writing a 0 leaves the others alone. That is a single write, so interrupts stay enabled. Onlyloop()changes a relay's bit, so reading its state first is safe.togglePin()already works this way for the watchdog pin.This could only happen with a relay on the loads' port (D2-D7), so not with the default pins.
Where the rest of the relay cost goes
With two relays, about 1.5 KB remains:
printRelayConfiguration()and its strings) - user-facing text, left as is;Test plan
test_utils_relayandtest_utils_pins(simavr): 47/47, includingtest_setPinStatetry_switch(): one load fromPORTx, one store toPINx, nocli, no store toPORTx🤖 Generated with Claude Code