Repository navigation
Hot path: debug-only type checks, lazy logging, direct component creation - #100
Open
andre-merzky wants to merge 4 commits into
Open
andre-merzky wants to merge 4 commits into
andre-merzky wants to merge 4 commits into
Conversation
…tion Per-task overhead outside the scheduler dominated throughput after #91: profiled at 8,192 workflows, ~45% of the time was typeguard checks on task_callbacks, plus eager log formatting, telemetry argument building with telemetry off, and a @wraps closure built for every task call. - task_callbacks is type-checked only in debug mode: RADICAL_ASYNCFLOW_DEBUG, else RADICAL_DEBUG (1/true/yes/on), read at import. The test suite enables it in tests/conftest.py. __init__ stays type-checked (once per engine). - Per-task log calls use lazy arguments or an isEnabledFor guard; the per-task state line and the per-batch "Submitting" line move from INFO to DEBUG. - Telemetry call sites in registration, dependency resolution and the run loop build their arguments only when telemetry is enabled. - A decorated call creates its component via _create_component instead of building and calling a @wraps wrapper per call; the async check runs once per decorated function. - init_default_logger sets the logger level to its lowest handler level instead of NOTSET, so records no handler emits are not built. - docs/best_practice.md documents debug mode and replaces the RADICAL_LOG_LVL hint (never read by asyncflow); CHANGELOG updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The decorator raises "must be async" itself; _create_component only creates components. - init_default_logger: a handler at NOTSET maps the logger level to 1 (accept all) rather than 0, which means "inherit" on named loggers. - CHANGELOG states the root and named logger effects of the level change; docs name task_callbacks as the switched method. - Tests: the sync-function rejection goes through the decorator; the type-check test skips on the import-time state; conftest imports stay at the top. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- _debug_typechecked names when the checks apply (matches _debug_enabled). - _notify_dependents: telemetry check joins the existing condition. - The decorator's async check is the first statement of the call path. - Docs drop a measured figure that would go stale; CHANGELOG entry for the logger level is shorter; test comment and imports tidied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
andre-merzky
marked this pull request as ready for review
October 13, 2026 20:39
This branch has not been deployed
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.
Depends on #91 (based on
feature/nosleep); retargeted tomainonce #91 is merged.Summary
After #91 removed the scheduler's sleeps and per-pass scans, task throughput is bound by per-task overhead outside the scheduler. Profiled at 8,192 concurrent workflows (
NoopExecutionBackend), about 45% of the time went totypeguardchecks ontask_callbacks, the rest largely to log formatting, telemetry arguments built with telemetry off, and a@wrapsclosure built for every task call. Withinit_default_logger()configured, every DEBUG call also built a fullLogRecord, because the logger level wasNOTSETand filtering happened only at the handlers.Changes:
task_callbacksis type-checked only whenRADICAL_ASYNCFLOW_DEBUGis set to1/true/yes/on; if that variable is unset,RADICAL_DEBUGdecides. Read at import.__init__stays type-checked (once per engine). The test suite enables debug mode intests/conftest.py, so CI keeps the checks.%sarguments, or anisEnabledFor(DEBUG)guard where an argument list is built. The per-task state line (<uid> is in <state> state) and the per-batchSubmitting <n> tasks/blocksline move from INFO to DEBUG.init_default_loggersets the logger level to the lowest level of its handlers instead ofNOTSET, so records no handler emits are dropped before they are built._create_componentinstead of building and calling a@wrapswrapper per call; the async check runs once per decorated function (inspect.iscoroutinefunction).docs/best_practice.mddocuments debug mode and replaces theRADICAL_LOG_LVLhint, which asyncflow never reads. CHANGELOG lists the INFO→DEBUG moves and the logger level change.Behaviour changes
init_default_loggerwith the root logger: records of other libraries below the configured level are dropped unless the library sets a level on its own logger. Withlogger_name, records at the configured level are now emitted (they were filtered by the root logger's level) and propagate to root handlers.task_callbacksarguments raiseTypeCheckErroronly in debug mode.inspect.iscoroutinefunction(AsyncMock())isFalse, so decorating anAsyncMockraises "must be async" there (3.10+ unaffected; no test does this).Performance
Laptop, one pinned core,
NoopExecutionBackendunless noted, environment withoutRADICAL_DEBUG; K = long-running tasks parked inrunning. Throughput: median of 10 runs; latency: median of 5. Markers compare to #91 head (this PR's base): 🟢 > 5% better, 🔴 > 5% worse, ⚪ within ±5%. Release 0.5.1 for reference.Throughput on the
01-workflows.pyworkload (tasks/s), no logging configured:Throughput with
init_default_logger(logging.INFO)(as in the ROSE examples and DT scripts):Latency, no logging configured (in parentheses: with
init_default_logger(logging.INFO)):LocalExecutionBackend(ms)LocalExecutionBackend(s)Idle CPU is unchanged from #91 head (
poll_timeout0.1 s: 0.0 / 1.2 / 7.3% of one core at K = 0 / 1,000 / 10k).Tests
tests/unit/test_debug_mode.py: variable parsing and precedence;task_callbacksraisesTypeCheckErrorin debug mode; logger level follows the lowest handler level.tests/unit/test_task_registration_component.py:_create_componentregisters a function task; a decorated sync function is rejected at call time.Not in this PR
🤖 Generated with Claude Code