Skip to content

perf: reduce background CPU churn and battery usage. - #859

Merged
mpfaffenberger merged 1 commit into
mpfaffenberger:mainfrom
StarsExpress:perf-reduce-cpu-churn
Aug 27, 2026
Merged

perf: reduce background CPU churn and battery usage.#859
mpfaffenberger merged 1 commit into
mpfaffenberger:mainfrom
StarsExpress:perf-reduce-cpu-churn

Conversation

@StarsExpress

@StarsExpress StarsExpress commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Refs issue #438 to decrease busy-wait polling in code_puppy/messaging/bus.py: get_message(), get_command().


PR #859's choice

Consider a real handoff: keep an asyncio.Queue bridged via call_soon_threadsafe (as message_queue.py already does), or at minimum back off the sleep when idle.

PR #859 implements the latter option — back off sleep interval when idle — as an alleviation for background CPU churn and battery usage.

Full fix to bridge code_puppy/tools/command_runner.py's background threading.Thread — used for streaming subprocess output — into the event loop via loop.call_soon_threadsafe is a larger and thus separate follow-up, since a straight swap to asyncio.Queue would break these cross-thread writes.


Alleviation implementations

code_puppy/messaging/bus.py: MessageBus now has a shared static method for get_message() and get_command():

T = TypeVar("T")

@staticmethod
async def _get_nowait_with_backoff(q: "queue.Queue[T]") -> T:
    """Poll a thread-safe queue with exponential backoff while idle.

    Wraps a sync queue in an asyncio-friendly way. The poll interval
    starts at 0.01s and doubles on each empty attempt up to a cap of
    0.1s, resetting to 0.01s on the next call (i.e. once a value is returned).
    """
    delay = 0.01

    while True:
        try:
            return q.get_nowait()

        except queue.Empty:
            await asyncio.sleep(delay)
            delay = min(delay * 2, 0.1)

async def get_message(self) -> AnyMessage:
    """Get the next outgoing message (async).

    Called by the renderer to consume messages.
    Blocks until a message is available.

    Returns:
        The next message to display.
    """
    return await self._get_nowait_with_backoff(self._outgoing)

async def get_command(self) -> AnyCommand:
    """Get the next incoming command (async).

    Called by the agent to consume commands (e.g., CancelAgentCommand).
    Blocks until a command is available.

    Returns:
        The next command to process.
    """
    return await self._get_nowait_with_backoff(self._incoming)

Idle CPU wakeups reduction

Before PR #859 Inside PR #859 Reduction %
454 52 88.5%

Tradeoffs

After empirically measuring for a message arriving after 0.4s of idle, additional latency introduced by this backoff is ~57.8ms.

Theoretical worst case is bounded by 0.1s backoff cap itself, since delay resets to 0.01s immediately after each successful read.


Additional adjustments

Inside code_puppy/messaging/bus.py, I added some blank lines to visually separate code chunks for better readability.

Also added some blank lines to enhance readability.
@StarsExpress
StarsExpress marked this pull request as ready for review August 23, 2026 20:18
@StarsExpress StarsExpress changed the title perf: reduced background CPU churn and battery usage. perf: reduce background CPU churn and battery usage. Aug 23, 2026
@mpfaffenberger
mpfaffenberger merged commit 78c6c10 into mpfaffenberger:main Aug 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants