Skip to content

[Data] [Core] [2.5/n] Remove BlockRefCounter.clear() and expand callback throughput benchmark - #64521

Open
rayhhome wants to merge 21 commits into
ray-project:masterfrom
rayhhome:callback-oos-api
Open

[Data] [Core] [2.5/n] Remove BlockRefCounter.clear() and expand callback throughput benchmark#64521
rayhhome wants to merge 21 commits into
ray-project:masterfrom
rayhhome:callback-oos-api

Conversation

@rayhhome

@rayhhome rayhhome commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Remove BlockRefCounter.clear() and its shutdown wiring
  • Expand callback throughput release benchmark

Description

Removes BlockRefCounter.clear() and the _block_ref_counter.clear() call in StreamingExecutor.shutdown(). The clear() method was added for deterministic cleanup on executor shutdown, but profiling and benchmarking showed:

  1. Blocks are freed naturally via out-of-scope callbacks when their ObjectRefs go out of scope. Explicit clearing is unnecessary.
  2. Removing clear() had no measurable impact on release test benchmarks.

Also expands test_callback_throughput.py (previously only had an incremental pipeline test) with additional benchmark tests at multiple scales (100 to 10,000 blocks):

  • Registration cost: on_block_produced overhead via the Data-layer path
  • Incremental pipeline: produce-consume-release, one block at a time
  • Burst drop: all refs dropped at once, measures total drain time
  • Per-callback latency: refs dropped one at a time with per-block timestamps
  • BlockRefCounter burst drop: same as burst drop but through the full BRC path

Related issues

Depends on #64157 (BlockRefCounter implementation).
Related to #63601 (prototype), #63074 (previous manual BlockRefCounter).

@rayhhome rayhhome self-assigned this Jul 2, 2026
@rayhhome rayhhome added core Issues that should be addressed in Ray Core data Ray Data-related issues go add ONLY when ready to merge, run all tests labels Jul 2, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@rayhhome
rayhhome marked this pull request as ready for review July 2, 2026 23:13
@rayhhome
rayhhome requested a review from a team as a code owner July 2, 2026 23:13
Copilot AI review requested due to automatic review settings July 2, 2026 23:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a symmetric “remove” API for Ray Core’s object out-of-scope/freed callback mechanism so callers (notably Python) can explicitly deregister callbacks and trigger their cleanup logic deterministically, rather than relying on callbacks becoming lazy no-ops.

Changes:

  • Introduces ReferenceCounterInterface::RemoveObjectOutOfScopeOrFreedCallbacks and implements it in ReferenceCounter.
  • Plumbs the API through CoreWorker and exposes it to Python via libcoreworker.pxd and _raylet.pyx.
  • Adds a C++ unit test covering “remove fires once; natural out-of-scope does not fire again”.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/ray/core_worker/tests/reference_counter_test.cc Adds a unit test validating removal fires callbacks exactly once.
src/ray/core_worker/reference_counter.h Declares the new removal API on the concrete reference counter.
src/ray/core_worker/reference_counter.cc Implements callback firing + clearing for the new removal API.
src/ray/core_worker/reference_counter_interface.h Adds the new API to the core reference counter interface and documents behavior.
src/ray/core_worker/core_worker.h Declares a CoreWorker wrapper for the new removal API.
src/ray/core_worker/core_worker.cc Implements the CoreWorker wrapper by delegating to the reference counter.
python/ray/includes/libcoreworker.pxd Exposes the CoreWorker removal method to Cython.
python/ray/_raylet.pyx Adds a Python-facing internal method to trigger callback removal/cleanup.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +332 to +334
/// Removes and fires all out-of-scope/freed callbacks for `object_id`.
/// Firing allows per-callback cleanup (e.g. Py_DECREF on the Python side).
/// After this call, no further out-of-scope callbacks will fire for this object.
Comment thread python/ray/_raylet.pyx Outdated
Comment on lines +4304 to +4308
"""Remove and fire all out-of-scope callbacks for the given object.

Firing allows per-callback cleanup (e.g. Py_DECREF). After this
call, no further out-of-scope callbacks will fire for this object.

@rayhhome rayhhome changed the title [Data] [Core] [2.5/n] Add RemoveObjectOutOfScopeOrFreedCallbacks API for Callback Cleanup [Data] [Core] [2.5/n] Unregister Out-of-scope Callback when clear is Called Jul 2, 2026
@rayhhome rayhhome changed the title [Data] [Core] [2.5/n] Unregister Out-of-scope Callback when clear is Called [Data] [Core] [2.5/n] Deregister Out-of-scope Callback when clear is Called Jul 2, 2026
rayhhome added 2 commits July 2, 2026 22:31
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>
@rayhhome
rayhhome force-pushed the callback-oos-api branch from 200944a to c3a6c68 Compare July 3, 2026 05:37

@Kunchd Kunchd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have any big concerns with this PR, but I'm struggling to determine the use case for this. We currently clean up the callback when the object goes out of scope and the callback is invoked. Why is explicit clean up necessary? Could we include the motivation in the PR description?

Comment on lines +895 to +897
for (const auto &callback : it->second.on_object_out_of_scope_or_freed_callbacks) {
callback(object_id);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is invoking all the callbacks neecessary before removing the callbacks? Doesn't removing the callback mean we don't care about them anymore?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BlockRefCounter's clear() is called when StreamingExecutor calls shutdown(), which happens when a dataset execution ends. In multi-epoch training (or any jobs with many dataset executions), this would be triggered many times. Since the callbacks have Py_DECREF to preserve the _on_object_freed closures, not triggering them would result in minor leaks. What's your take on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we expect object produced within a specific epoch to stay around after the epoch finishes?

@rayhhome rayhhome left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is mainly for aligning the behavior of clear() with what the function name suggests. In the current implementation, only the Data side dictionary is cleared when clear() is called. The Core callbacks will still be fired when ObjectRefs go out of scope, which seems unintuitive based on what the function is intended to do.

However, I think this does raise the question on whether we need the clear() function at all, considering that no code reads the accounting after the shutdown. Will check this offline and come back with the decision.

Comment on lines +895 to +897
for (const auto &callback : it->second.on_object_out_of_scope_or_freed_callbacks) {
callback(object_id);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BlockRefCounter's clear() is called when StreamingExecutor calls shutdown(), which happens when a dataset execution ends. In multi-epoch training (or any jobs with many dataset executions), this would be triggered many times. Since the callbacks have Py_DECREF to preserve the _on_object_freed closures, not triggering them would result in minor leaks. What's your take on this?

@rayhhome
rayhhome requested a review from a team as a code owner July 14, 2026 21:02
rayhhome added 2 commits July 14, 2026 16:18
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>
Comment thread src/ray/core_worker/reference_counter.cc Outdated
Comment thread src/ray/core_worker/reference_counter.cc Outdated
@rayhhome
rayhhome requested a review from a team as a code owner July 18, 2026 00:12
Comment thread release/benchmarks/object_store/test_callback_throughput.py Outdated
Comment thread python/ray/_raylet.pyx Outdated
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 3548794. Configure here.

Comment thread src/ray/core_worker/core_worker.h Outdated
Comment thread python/ray/data/_internal/execution/streaming_executor.py
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>
rayhhome added 9 commits July 20, 2026 16:46
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>
Signed-off-by: Sirui Huang <ray.huang@anyscale.com>
@rayhhome rayhhome changed the title [Data] [Core] [2.5/n] Deregister Out-of-scope Callback when clear is Called [Data] [Core] [2.5/n] Remove BlockRefCounter.clear() and expand callback throughput benchmark Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Issues that should be addressed in Ray Core data Ray Data-related issues go add ONLY when ready to merge, run all tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants